问题描述
我是一名初学者程序员,致力于解决C ++中的2019年代码问世挑战。
我整理的最后一个难题是实际上使程序读取input.txt文件,该文件本质上是一长串字符串,格式为'10,20,40,23“等。在一行上。
在上一个难题中,我使用了线条
int inputvalue;
std::ifstream file("input.txt");
while(file >> inputvalue){
//
}
可以从文件中抓取行,但是它被格式化为文本文件,并且没有逗号分隔。
即:
10
20
40
23
我该怎么做才能使用逗号分隔来读取文件,特别是如何将这些值读取为整数而不是字符串或char并将其存储到向量中?
解决方法
您可以选择。我认为,最直接的方法是只读取一个字符串,然后转换为整数。您可以使用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();
当然,以上所有方法都是这样做的笨拙方式:
// Skip characters up to (and including) next comma on next read
file.ignore(std::numeric_limits<std::streamsize>::max(),');
所有这些方法均假定输入为一行。如果您希望输入的行有逗号分隔的值,则还需要处理行末 而不会遇到逗号。否则,您可能会错过下一行的第一个输入。除了“有希望的”方法,该方法可以工作,但仅在技术上有效。
出于鲁棒性,我通常建议您使用std::getline
将基于行的输入作为整个字符串读取,然后使用std::istringstream
从该行中读取各个值。
虽然编写一个例程以从逗号分隔的文件中仅读取一行是很奇怪的,而不是编写一个通用例程以读取所有行(如果只想要一行,则只取第一行),这很奇怪–取出将多行读入std::vector<std::vector<int>>
的部分,仅将一行读入std::vector<int>
-尽管它只保存了几行代码。
一般方法是使用getline(file,line)
读取整行文本,然后创建一个std::stringstream (line)
,然后可以使用>>
读取每个整数,然后是{{ 1}}来读取分隔符。
除了要读取的文件外,您还可以使用第二个参数,因此可以将定界符作为第二个参数的第一个字符传递-这样,就没有理由重新编译代码来处理{ {1}}或getline (file,tmpstr,')
或任何其他单个字符。
您可以将一小段代码放在一起,以执行以下操作:
';'
(注意:将所有行读入向量vector所需的更改相对较少,更值得注意的是将','
替换为#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..)
使用/输出示例
您使用的结果将是:
dat/int-1-10-1-line.txt
当然,您可以将输出格式更改为所需的格式。仔细研究一下,如果您还有其他问题,请告诉我。
,这是另一个使用迭代器的紧凑型解决方案。
#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;
}