解析字符串比解析文件中的任意数据更容易。从文件中获取单行信息并将其存储到std::string 或读取所有文件并将内容保存到大的buffer 或std::vector<std::string>> 中更容易。然后在获得所需的所有信息后,关闭文件句柄,然后就可以进行解析了。
在一些std libraries 的帮助下,您可以相当容易地做到这一点。我们还将使用辅助函数来分解文件中的文本行。
#include <numeric>
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
std::vector<std::string> split( const std::string& s, char delimiter ) {
std::vector<std::string> tokens = {};
std::string token = {};
std::istringstream tokenStream( s ); // std::istringstream found in <sstream>
while( std::getline( tokenStream, token, delimiter ) ) {
tokens.push_back( token );
}
return tokens;
}
int number_sum( const char* filename ) {
// First try to open the file; if fails return -1
std::ifstream file;
file.open( filename );
if ( !file.is_open() ) {
std::cout << "failed to open file " << filename << '\n';
return -1;
}
// read a single line from the file and save it to a local string
std::string line = {};
std::getline( file, line );
// close the file
file.close();
// now parse the local string into string tokens and convert them to ints
std::vector<int> values = {};
std::vector<std::string> tokens = split( line, ',' );
for ( auto s : tokens ) {
values.push_back( std::stoi( s ) ); // std::stoi() found in <string>
}
// now that we have our vector of ints parsed from the strings we can add them together
// std::accumulate() found in <numeric>
return std::accumulate( values.begin(), values.end(), 0 );
}
int main() {
std::cout << number_sum( "test.txt" );
return 0;
}
test.txt
2,3,4,1
输出
10
使用这种方法,您不必担心是否存在分隔符,这将适用于奇数和偶数类型的情况。当您知道要使用哪些函数时,这就是 stl 库的强大功能。
现在这只会从您的输入文件中执行一行,但可以扩展它以合并来自文件的多行,其中包含一个简单的 while 循环和一个额外的向量来存储每一行。不过,您可能必须更改此函数返回的内容。我会把这部分留给你做练习。
在此答案的先前迭代中,我曾提到有一个错误并且我知道它是什么;当我最初发布它时,我正在编写一个辅助函数。只要逗号之间的值是单个数字字符,该程序对每个字符都可以正常工作,并且如果逗号之间有多个值,它将失败或中断。借助此辅助函数通过分隔符将字符串拆分为多个字符串,我们不必担心手动解析字符串中的每个字符,因为 stl 库和函数将为我们完成此操作。此功能现在可以正常工作,并且适用于逗号之间的值超过一位的情况!
test.txt - 2nd 试用
23,32,46,11
输出
112
经过一番考虑,我稍微清理了一下。我不喜欢这样一个事实,即进行累积的函数负责打开和读取文件中的内容,所以我将它移到了它自己的单独函数中。我还喜欢在运行时捕获错误并将它们显示到控制台的能力。最后,我重命名了一个函数,使其更具可读性。这是重构后的程序:
#include <numeric>
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <exception>
std::string readLineFromFile( const char* filename ) {
std::ifstream file( filename );
if ( !file ) {
std::stringstream stream;
stream << "failed to open file " << filename << '\n';
throw std::runtime_error( stream.str() );
}
std::string line;
std::getline( file, line );
file.close();
return line;
}
std::vector<std::string> splitString( const std::string& s, char delimiter ) {
std::vector<std::string> tokens;
std::string token;
std::istringstream tokenStream( s );
while( std::getline( tokenStream, token, delimiter ) ) {
tokens.push_back( token );
}
return tokens;
}
int number_sum( const char* filename ) {
// Get contents from file
std::string line = readLineFromFile( filename );
// parse string
std::vector<int> values;
std::vector<std::string> tokens = splitString( line, ',' );
for( auto s : tokens ) {
values.push_back( std::stoi( s ) );
}
// return the accumulated value
return std::accumulate( values.begin(), values.end(), 0 );
}
int main() {
try {
std::cout << number_sum( "test.txt" ) << '\n';
// assuming there is no "test2.txt"
// this will throw a runtime error
// and display the appropriate message to the console
std::cout << number_sum( "test2.txt" ) << '\n';
} catch( const std::runtime_error& e ) {
std::cerr << e.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}