(C++) 将 CSV 文本文件读取为整数向量
(C++) Reading a CSV text file as a vector of integers
我是一名初学者,正在应对 2019 年 C++ 代码问世挑战。
我拼凑的最后一块拼图实际上是让程序读取 input.txt 文件,它本质上是一长串值,形式为 '10,20,40, 23" 等在一条线上。
在之前的拼图中我使用了线条
int inputvalue;
std::ifstream file("input.txt");
while(file >> inputvalue){
//
}
从文件中抓取行,但它被格式化为连续行的文本文件,没有逗号分隔。
即:
10
20
40
23
如何使用逗号分隔读取文件,特别是如何将这些值读取为整数,而不是字符串或字符,并将它们存储到向量中?
你有选择。在我看来,最straight-forward就是只读取一个字符串,然后转为整数。可以使用std::getline
的附加“定界符”参数,遇到逗号就停止:
std::string value;
while (std::getline(file, value, ',')) {
int ival = std::stoi(value);
std::cout << ival << std::endl;
}
一个常见的替代方法是读取单个字符,期望它是一个逗号:
int ival;
while (file >> ival) {
std::cout << ival << std::endl;
// Skip comma (we hope)
char we_sure_hope_this_is_a_comma;
file >> we_sure_hope_this_is_a_comma;
}
如果空格也可能存在,您可能需要一种不太“有希望”的技术来跳过逗号:
// Skip characters up to (and including) next comma
for (char c; file >> c && c != ',';);
或者简单地说:
// Skip characters up to (and including) next comma
while (file && file.get() != ',');
或者实际上,如果您希望 只有 空格 或 一个逗号,您可以这样做:
// Skip comma and any leading whitespace
(file >> std::ws).get();
当然,以上都是more-or-less笨拙的做法:
// Skip characters up to (and including) next comma on next read
file.ignore(std::numeric_limits<std::streamsize>::max(), ',');
所有这些方法都假设输入是一行。如果您希望多行输入具有 comma-separated 值,您还需要处理 end-of-line 出现 而不会 遇到逗号。否则,您可能会错过下一行的第一个输入。除了“充满希望”的方法,它会起作用,但仅在技术上有效。
为了稳健性,我通常建议您使用 std::getline
将 line-based 输入作为整个字符串读取,然后使用 std::istringstream
从该行中读取单个值。
虽然编写一个只从逗号分隔的文件中读取一行的例程,而不是编写一个读取所有行的通用例程(如果您只想要一行,则只取第一行)会很奇怪 --您可以取出用于将多行读入 std::vector<std::vector<int>>
的部分,而只将一行读入 std::vector<int>
—— 尽管它只节省了少量代码行。
一般方法是使用 getline(file, line)
读取整行文本,然后创建一个 std::stringstream (line)
,然后您可以使用 >>
读取每个整数后跟一个getline (file, tmpstr, ',')
读取分隔符。
除了要读取的文件之外,您还可以使用第二个参数,这样您就可以将定界符作为第二个参数的第一个字符传递——这样就没有理由 re-compile 您的代码处理 ';'
或 ','
或任何其他单个字符的分隔符。
您可以将一小段代码放在一起来执行以下操作:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
int main (int argc, char **argv) {
if (argc < 2) { /* validate at least 1 argument given */
std::cerr << "error: insufficient number of arguments.\n"
"usage: " << argv[0] << " <filename>\n";
return 1;
}
std::vector<int> v {}; /* vector<int> */
std::string line {}; /* string to hold each line */
std::ifstream f (argv[1]); /* open file-stream with 1st argument */
const char delim = argc > 2 ? *argv[2] : ','; /* delim is 1st char in */
/* 2nd arg (default ',') */
if (!f.good()) { /* validate file open for reading */
std::cerr << "errro: file open failed '" << argv[1] << "'.\n";
return 1;
}
if (getline (f, line)) { /* read line of input into line */
int itmp; /* temporary integer to fill */
std::stringstream ss (line); /* create stringstream from line */
while (ss >> itmp) { /* read integer value from ss */
std::string stmp {}; /* temporary string to hold delim */
v.push_back(itmp); /* add to vector */
getline (ss, stmp, delim); /* read delimiter */
}
}
for (auto col : v) /* loop over each integer */
std::cout << " " << col; /* output col value */
std::cout << '\n'; /* tidy up with newline */
}
(注意: 将所有行读入向量的向量所需的更改相对较少,更值得注意的是简单地将 if(getline...)
替换为 while(getline..)
然后填充一个临时向量,如果 non-empty,然后将其推回到您的向量集合中)
示例输入文件
在名为 dat/int-1-10-1-line.txt
的文件中使用一组逗号分隔的整数,例如
$ cat dat/int-1-10-1-line.txt
1,2,3,4,5,6,7,8,9,10
例子Use/Output
您的使用结果将是:
$ ./bin/read_csv_int-1-line dat/int-1-10-1-line.txt
1 2 3 4 5 6 7 8 9 10
当然,您可以将输出格式更改为您需要的任何格式。检查一下,如果您还有其他问题,请告诉我。
这是另一个使用迭代器的紧凑解决方案。
#include <iostream>
#include <vector>
#include <string>
#include <iterator>
#include <fstream>
#include <algorithm>
template <char D>
struct WordDelimiter : public std::string
{};
template <char D>
std::istream &
operator>>(std::istream & is, WordDelimiter<D> & output)
{
// Output gets every comma-separated token
std::getline(is, output, D);
return is;
}
int main() {
// Open a test file with comma-separated tokens
std::ifstream f{"test.txt"};
// every token is appended in the vector
std::vector<std::string> vec{ std::istream_iterator<WordDelimiter<','>>{ f },
std::istream_iterator<WordDelimiter<','>>{} };
// Transform str vector to int vector
// WARNING: no error checking made here
std::vector<int> vecint;
std::transform(std::begin(vec),std::end(vec),std::back_inserter(vecint),[](const auto& s) { return std::stoi(s); });
for (auto val : vecint) {
std::cout << val << std::endl;
}
return 0;
}
我是一名初学者,正在应对 2019 年 C++ 代码问世挑战。
我拼凑的最后一块拼图实际上是让程序读取 input.txt 文件,它本质上是一长串值,形式为 '10,20,40, 23" 等在一条线上。
在之前的拼图中我使用了线条
int inputvalue;
std::ifstream file("input.txt");
while(file >> inputvalue){
//
}
从文件中抓取行,但它被格式化为连续行的文本文件,没有逗号分隔。
即:
10
20
40
23
如何使用逗号分隔读取文件,特别是如何将这些值读取为整数,而不是字符串或字符,并将它们存储到向量中?
你有选择。在我看来,最straight-forward就是只读取一个字符串,然后转为整数。可以使用std::getline
的附加“定界符”参数,遇到逗号就停止:
std::string value;
while (std::getline(file, value, ',')) {
int ival = std::stoi(value);
std::cout << ival << std::endl;
}
一个常见的替代方法是读取单个字符,期望它是一个逗号:
int ival;
while (file >> ival) {
std::cout << ival << std::endl;
// Skip comma (we hope)
char we_sure_hope_this_is_a_comma;
file >> we_sure_hope_this_is_a_comma;
}
如果空格也可能存在,您可能需要一种不太“有希望”的技术来跳过逗号:
// Skip characters up to (and including) next comma
for (char c; file >> c && c != ',';);
或者简单地说:
// Skip characters up to (and including) next comma
while (file && file.get() != ',');
或者实际上,如果您希望 只有 空格 或 一个逗号,您可以这样做:
// Skip comma and any leading whitespace
(file >> std::ws).get();
当然,以上都是more-or-less笨拙的做法:
// Skip characters up to (and including) next comma on next read
file.ignore(std::numeric_limits<std::streamsize>::max(), ',');
所有这些方法都假设输入是一行。如果您希望多行输入具有 comma-separated 值,您还需要处理 end-of-line 出现 而不会 遇到逗号。否则,您可能会错过下一行的第一个输入。除了“充满希望”的方法,它会起作用,但仅在技术上有效。
为了稳健性,我通常建议您使用 std::getline
将 line-based 输入作为整个字符串读取,然后使用 std::istringstream
从该行中读取单个值。
虽然编写一个只从逗号分隔的文件中读取一行的例程,而不是编写一个读取所有行的通用例程(如果您只想要一行,则只取第一行)会很奇怪 --您可以取出用于将多行读入 std::vector<std::vector<int>>
的部分,而只将一行读入 std::vector<int>
—— 尽管它只节省了少量代码行。
一般方法是使用 getline(file, line)
读取整行文本,然后创建一个 std::stringstream (line)
,然后您可以使用 >>
读取每个整数后跟一个getline (file, tmpstr, ',')
读取分隔符。
除了要读取的文件之外,您还可以使用第二个参数,这样您就可以将定界符作为第二个参数的第一个字符传递——这样就没有理由 re-compile 您的代码处理 ';'
或 ','
或任何其他单个字符的分隔符。
您可以将一小段代码放在一起来执行以下操作:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
int main (int argc, char **argv) {
if (argc < 2) { /* validate at least 1 argument given */
std::cerr << "error: insufficient number of arguments.\n"
"usage: " << argv[0] << " <filename>\n";
return 1;
}
std::vector<int> v {}; /* vector<int> */
std::string line {}; /* string to hold each line */
std::ifstream f (argv[1]); /* open file-stream with 1st argument */
const char delim = argc > 2 ? *argv[2] : ','; /* delim is 1st char in */
/* 2nd arg (default ',') */
if (!f.good()) { /* validate file open for reading */
std::cerr << "errro: file open failed '" << argv[1] << "'.\n";
return 1;
}
if (getline (f, line)) { /* read line of input into line */
int itmp; /* temporary integer to fill */
std::stringstream ss (line); /* create stringstream from line */
while (ss >> itmp) { /* read integer value from ss */
std::string stmp {}; /* temporary string to hold delim */
v.push_back(itmp); /* add to vector */
getline (ss, stmp, delim); /* read delimiter */
}
}
for (auto col : v) /* loop over each integer */
std::cout << " " << col; /* output col value */
std::cout << '\n'; /* tidy up with newline */
}
(注意: 将所有行读入向量的向量所需的更改相对较少,更值得注意的是简单地将 if(getline...)
替换为 while(getline..)
然后填充一个临时向量,如果 non-empty,然后将其推回到您的向量集合中)
示例输入文件
在名为 dat/int-1-10-1-line.txt
的文件中使用一组逗号分隔的整数,例如
$ cat dat/int-1-10-1-line.txt
1,2,3,4,5,6,7,8,9,10
例子Use/Output
您的使用结果将是:
$ ./bin/read_csv_int-1-line dat/int-1-10-1-line.txt
1 2 3 4 5 6 7 8 9 10
当然,您可以将输出格式更改为您需要的任何格式。检查一下,如果您还有其他问题,请告诉我。
这是另一个使用迭代器的紧凑解决方案。
#include <iostream>
#include <vector>
#include <string>
#include <iterator>
#include <fstream>
#include <algorithm>
template <char D>
struct WordDelimiter : public std::string
{};
template <char D>
std::istream &
operator>>(std::istream & is, WordDelimiter<D> & output)
{
// Output gets every comma-separated token
std::getline(is, output, D);
return is;
}
int main() {
// Open a test file with comma-separated tokens
std::ifstream f{"test.txt"};
// every token is appended in the vector
std::vector<std::string> vec{ std::istream_iterator<WordDelimiter<','>>{ f },
std::istream_iterator<WordDelimiter<','>>{} };
// Transform str vector to int vector
// WARNING: no error checking made here
std::vector<int> vecint;
std::transform(std::begin(vec),std::end(vec),std::back_inserter(vecint),[](const auto& s) { return std::stoi(s); });
for (auto val : vecint) {
std::cout << val << std::endl;
}
return 0;
}