概要
逐行读取文本文件,并提取其中连续的几行,这对于 Python 来说是小菜一碟。 C++ 则很笨拙, 语言不自带这些。 这次我来拯救 C++ boys & girls, 在 C++20 环境下,山寨一个 Python 下的逐行读文本文件、支持 slice 操作的代码,包含基础设施的实现和调用实例代码。
问题是什么?
data.csv
0,11,336,23,370
0,5,370,16,404
1,370,404,410,419
1,435,376,444,402
2,249,409,280,446
2,579,441,632,472
python">filepath = "data.csv"with open(filepath, "r") as f:# print('type(f):', type(f))lines = f.readlines()for line in lines[1:3]:print(line.strip())print('---')for line in lines[3:]:print(line.strip())
对于上述文本文件和 Python 代码, 用 C++20 实现一个等效的代码:
- 基础设施代码,不限制行数
- 调用代码,尽可能和 Python 代码长得像, 比如都是10行以内,切片操作也尽可能的直观
逐行读取文件 - C++ 实现
这个功能比较好实现,返回 vector 就可以了。文件读取使用的是 std::fstream,基于 RAII 思想创建和释放。对于每一行文本的读取,使用 std::getline() 来完成。
#include <fstream>
#include <vector>
#include <string>class TextIOWrapper
{
public:TextIOWrapper(const std::string& filepath){ifs.open(filepath);}~TextIOWrapper(){ifs.close();}std::string readline(){std::string line;std::getline(ifs, line);return line;}std::vector<std::string> readlines(){std::vector<std::string> lines;std::string line;while (std::getline(ifs, line)){lines.emplace_back(line);}return lines;}private:std::ifstream ifs;
};
切片操作 - 基于C++20的实现
对于切片操作: Python 中的 [start:end]
, C++ 里并不支持; std::ranges
使用的管道操作符 |
,可以用作这个目的。
std::ranges 笨拙的地方在于,只提供了 std::ranges::views::drop(start)
或 std::ranges::views::take(end-start)
这样的基础操作,从来不考虑调用者用起来多费劲。。。那么我们自行封装一个:
namespace views {auto slice(size_t start, size_t end) {return std::ranges::views::drop(start) | std::ranges::views::take(end - start);}auto slice(size_t start) {return std::ranges::views::drop(start);}
}
调用代码 - C++
#include <iostream>int main()
{const std::string filepath = "data.csv";TextIOWrapper fin(filepath);auto lines = fin.readlines();for (auto line : lines | views::slice(1, 3)){std::cout << line << std::endl;}printf("---\n");for (auto line : lines | views::slice(3)){std::cout << line << std::endl;}return 0;
}
完整代码
https://github.com/zchrissirhcz/clumsy/commit/9273ccaf50c31a5b0c2190713dd696c7d0d200f4
总结
本文从简洁的 Python 读取文本文件和切片的代码出发,以相同简洁的调用代码为目标,使用常规的 C++ 封装方式实现了返回文件所有行或单独一行的操作;然后基于 C++20 的 std::ranges 实现了类似 Python 中的切片操作, 并给出了示例代码来展示正确性。
参考
https://stackoverflow.com/questions/50549611/slicing-a-vector-in-c