Как получить последнее целое число последовательности целых чисел, разделенных запятыми в C ++? - PullRequest
0 голосов
/ 29 октября 2018

Я пытаюсь получить каждое целое число из файла целых чисел, разделенных запятыми, и добавить их. Например, если файл содержит 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;
}

Это хорошо работает со всеми цифрами, кроме последней. Проблема возникает из-за того, что за последним не стоит запятая, поэтому программа его не получает. Я пытался различить значение «c», проверяя, является ли оно «,» или EOF, но это не сработало. Есть ли какое-нибудь решение, чтобы моя программа могла получить последний номер и добавить его к остальным? (Извините за мой английский, не родной).

Большое спасибо заранее.

Ответы [ 2 ]

0 голосов
/ 29 октября 2018

Проанализировать строку проще, чем произвольные данные из файла. Проще взять одну строку информации из файла и сохранить ее в 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 - 2 и пробная версия

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;
}
0 голосов
/ 29 октября 2018

Добавление следующей строки в этот код работает, но может потребоваться некоторое внимание для числа

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

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

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

Это похоже на грубое форсирование последнего, надеюсь, это поможет!

...