Может кто-нибудь сказать мне, что я сделал неправильно (cpp) форматирования с getline - PullRequest
1 голос
/ 31 января 2020

пытается отформатировать с помощью функции getline c ++. Вывод помещает все в первую запись с именем forename вместо того, где он должен go.

Код:

#include <fstream>
#include <string>
#include <iostream>
using namespace std;

int main()
{
    const int RANGE = 12;
    string tab[RANGE];
    int i = 0, j = 0;
    ifstream reader("records.txt");
    if (!reader)
    {
        cout << "Error opening input file" << endl;
        return -1;
    }
    while (!reader.eof())
    {
        if ( ( i + 1) % 4 == 0)
            getline( reader, tab[i++], '\n');
        else
            getline( reader, tab[i++], '\t');
    }
    reader.close();
    i = 0;
    while (i < RANGE)
    {
        cout << endl << "Record Number: " << ++j << endl;
        cout << "Forename: " << tab[i++] << endl;
        cout << "Surname: " << tab[i++] << endl;
        cout << "Department: " << tab[i++] << endl;
        cout << "Telephone: " << tab[i++] << endl;
    }
    return 0;
}

Содержимое файла TXT:

John    Smith    Sales    555-1234
Mary    Jones    Wages    555-9876
Paul    Harris   Accts    555-4321

Пожалуйста, запустите код для себя, чтобы понять, что происходит, и поместите файл txt в ту же папку как ваш код.

Надеюсь, кто-то может мне помочь, спасибо.

Ответы [ 4 ]

1 голос
/ 31 января 2020

См. Почему iostream :: eof внутри условия al oop (то есть, когда (! Stream.eof ())) считается неправильным? .

Кроме того, ваш окончательный результат while l oop должен выводить только строки, которые были фактически прочитаны в массив, а не полный массив, если файл содержит менее 12 строк. Но если вы не можете гарантировать, что ваш файл никогда не превышает 12 строк, вы должны использовать std::vector вместо фиксированного массива.

Кроме того, вместо чередования разделителя getline() в одном l oop, я будет просто использовать внешний l oop только для чтения целых строк, а затем отдельно читать значения с разделителями табуляции из каждой строки. А затем сохраните значения в массиве / векторе struct, а не по отдельности.

Попробуйте что-то вроде этого:

#include <fstream>
#include <sstream>
#include <string>
#include <iostream>
#include <vector>
using namespace std;

struct Person
{
    string foreName;
    string surName;
    string department;
    string phoneNumber;
};

int main()
{
    ifstream reader("records.txt");
    if (!reader)
    {
        cout << "Error opening input file" << endl;
        return -1;
    }

    vector<Person> people;
    string line;

    while (getline(reader, line))
    {
        istringstream iss(line);
        Person p;
        getline(iss, p.foreName, '\t');
        getline(iss, p.surName, '\t');
        getline(iss, p.department, '\t');
        getline(iss, p.phoneNumber, '\t');
        people.push_back(p);
    } 

    reader.close();

    int j = 0;
    for (Person &p : people)
    {
        cout << endl << "Record Number: " << ++j << endl;
        cout << "Forename: " << p.foreName << endl;
        cout << "Surname: " << p.surName << endl;
        cout << "Department: " << p.department << endl;
        cout << "Telephone: " << p.phoneNumber << endl;
    }

    return 0;
}
0 голосов
/ 31 января 2020

Во-первых, while (!reader.eof()) поступает неправильно .

Непосредственная проблема, которую вы видите, связана с тем, что ваш файл не содержит '\t', следовательно, уже самый первый getline считывает все содержимое файла в tab[0]. (По крайней мере, это то, что я получил после 1-к-1 копирования содержимого вашего файла)

Ваш код довольно сложен, потому что вы объявляете переменные задолго до того, как их используете, а затем снова используете. У вас есть массив фиксированного размера, но когда в файле будет больше строк, ваш код просто обработает sh. Также чтение всего в простой массив строк усложняет ситуацию. Доступ к forename или другим полям требует, чтобы вы вычислили смещение в массиве. Лучше использовать структуру данных:

struct file_entry {
    std::string first_name;
    std::string last_name;
    std::string departure;
    std::string phone;
};

Затем вы можете определить оператор ввода:

std::istream& operator>>(std::istream& in,file_entry& fe) {
    return in >> fe.first_name >> fe.last_name >> fe.departure >> fe.phone;
};

И использовать std::vector, чтобы сохранить столько записей, сколько имеется в файле:

int main() {
    std::string contents{"John    Smith    Sales    555-1234\n"
                         "Mary    Jones    Wages    555-9876\n"
                         "Paul    Harris   Accts    555-4321\n"};

    std::stringstream reader{contents};
    std::vector<file_entry> data;    
    std::string line;
    while (std::getline(reader,line)) {
        file_entry fe;
        std::stringstream{line} >> fe;
        data.push_back(fe);
    }

    for (const auto& fe : data) {
        std::cout << "Forename: " << fe.first_name << '\n';
        std::cout << "Surname: " << fe.last_name << '\n';
        std::cout << "Department: " << fe.departure << '\n';
        std::cout << "Telephone: " << fe.phone << '\n';
    }
}

живой пример

PS вам не нужно вызывать close для файла, это уже сделано в его деструкторе , Отсутствие явного вызова имеет то преимущество, что тот же код, который работает для файлового потока, также работает для потока строк.

0 голосов
/ 31 января 2020

Источник вашей проблемы, я думаю, объясняется в Почему iostream :: eof внутри условия al oop (то есть `while (! Stream.eof ())`) считается неправильным? .

Вы читаете в tab[12], tab[13], tab[13] и tab[14] из-за этой ошибки. Конечно, это приводит к неопределенному поведению.

Измените l oop на:

// Read the contents of the file line by line
std::string line;
while (getline( reader, line))
{
   // Process each line's contents.
   std::istringstream str(line);
   getline(str, tab[i++], '\t');
   getline(str, tab[i++], '\t');
   getline(str, tab[i++], '\t');
   getline(str, tab[i++], '\n');
}

Обязательно добавьте

#include <sstream>

Чтобы быть уверенным, что Вы не используете массив, используя индексы за пределами, добавьте проверку.

while ( i+4 < RANGE && getline( reader, line))
{
   ...
}
0 голосов
/ 31 января 2020

Существуют более простые способы разделения слов в istream, а именно инструменты потоковой передачи C ++:

#include <fstream>
#include <iostream>
#include <sstream>  //<-- string stream library

using namespace std; //<-- should not be used, use scope std::

int main() {

    const int RANGE = 12;
    string tab[RANGE];
    string temp;       //<--to store each field temporarily
    int i = 0, j = 0;
    ifstream reader("records.txt");
    if (!reader) {
        cout << "Error opening input file" << endl;
        return -1;
    }
    while (getline(reader, temp)) { //<-- read one full line
        stringstream ss(temp); // <-- input to a string stream
        while(ss >> tab[i]){   // <-- passing strings to the string array one by one
            i++;
        }
    }
    reader.close();
    i = 0;
    while (i < RANGE) {
        cout << endl << "Record Number: " << ++j << endl;
        cout << "Forename: " << tab[i++] << endl;
        cout << "Surname: " << tab[i++] << endl;
        cout << "Department: " << tab[i++] << endl;
        cout << "Telephone: " << tab[i++] << endl;
    }
    return 0;
}

Идея состояла в том, чтобы как можно меньше связываться с вашим кодом, я бы посоветовал: используйте std::vector вместо обычных массивов фиксированного размера. Кроме того, как было сказано и связано, eof очень ненадежно.

...