首页 > 解决方案 > 如何获取C++中用逗号分隔的整数序列的最后一个整数?

问题描述

我要做的是从用逗号分隔的整数文件中获取每个整数并添加它们。例如,如果文件包含2,3,4,1我的程序必须显示总和为 10。为此,我编写了以下代码。

int number_sum(const char* filename) {

std::ifstream file(filename); 

if (file.is_open()) {

    int sum = 0; //sum
    char c; //Store each comma
    int number = 0; //Store each number

    while ( (file >> number >> c) && (c == ',') ) {

        std::cout << "Adding up: " << number << " + " << sum << std::endl;
        sum = sum + number;


    }

    std::cout << "Result is: " << sum << std::endl;

    return sum;
}
else {
    std::cout << "ERROR" << std::endl;
    return -1;
}

这适用于除最后一位以外的所有数字。问题来了是因为最后一个后面没有逗号,所以程序没有搞定。我试图通过检查它是','还是EOF来区分“c”值,但这不起作用。有什么解决方案可以让我的程序获取最后一个数字并将其添加到其余数字中吗?(对不起我的英语,不是母语)。

非常感谢您提前。

标签: c++fileintegerifstream

解决方案


解析字符串比解析文件中的任意数据更容易。从文件中获取单行信息并将其存储到一个std::string或读取所有文件并将内容保存到一个大文件bufferstd::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;
}

测试.txt

2,3,4,1

输出

10

使用这种方法,您不必担心是否存在分隔符,这将适用于奇数和偶数类型的情况。stl当您知道要使用哪些函数时,这就是库的力量。

现在这只会从您的输入文件中执行一行,但是可以扩展它以将文件中的多行与一个简单的 while 循环和一个额外的向量合并来存储每一行​​。不过,您可能必须更改此函数返回的内容。我会把这部分留给你作为练习。


在此答案的先前迭代中,我曾提到有一个错误并且我知道它是什么;当我最初发布它时,我正在编写一个辅助函数。只要逗号之间的值是单个数字字符,该程序对每个字符都可以正常工作,并且如果逗号之间有多个值,它将失败或中断。借助此辅助函数通过分隔符将字符串拆分为多个字符串,我们不必担心手动解析字符串中的每个字符,因为stl库和函数将为我们完成此操作。此功能现在可以正常工作,并且适用于逗号之间的值超过一位的情况!

test.txt -第二次试用

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;
}

推荐阅读