Пустая строка в конце вывода

#c

#c

Вопрос:

Итак, у меня есть файл данных «db.csv». Код считывает каждое значение из файла, разделенное запятой (value1, …). Ненужные пробелы в строках удаляются, затем готовая строка распечатывается по желанию (один пробел между значениями). Проблема в том, что я должен пройти тесты на сайте под названием repl.it . И я проваливаю каждый тест, потому что в конце вывода есть дополнительная пустая строка. Есть предложения? Я знаю, что это, вероятно, из-за endl в cout, но как еще мне это сделать?

Минимальный воспроизводимый код:

 #include <iostream>
#include <fstream>
#include <algorithm>
#include <sstream>

using namespace std;

int main() {
    fstream file("db.csv", ios::in);
    string value1, value2, value3, value4, value5, line;

    cout << "result:" << endl;

    while (getline(file, line)) {
        if (line.length() > 1) {
            istringstream str1(line);

            while (getline(str1, value1, ','),
                   getline(str1, value2, ','),
                   getline(str1, value3, ','),
                   getline(str1, value4, ','),
                   getline(str1, value5, 'n')) {
                value1.erase(remove(value1.begin(), value1.end(), ' '), value1.end());
                value2.erase(remove(value2.begin(), value2.end(), ' '), value2.end());
                value3.erase(remove(value3.begin(), value3.end(), ' '), value3.end());
                value4.erase(remove(value4.begin(), value4.end(), ' '), value4.end());
                value5.erase(remove(value5.begin(), value5.end(), ' '), value5.end());

                cout << value1 << " " << value2 << " " 
                     << value3 << " " << value4 << " " 
                     << value5 
                     << endl;
            }
        }
    } 
}
 

данные файла db.csv:

 Riga,Kraslava,Pr,15:00,11.00

Riga ,Kraslava,Pr ,18:00,11.00
   Kraslava,Riga,Pr,08:00,11.00
Kraslava,Daugavpils,Ot ,10:00, 3.00
Ventsplis,8.00,Liepaja,Sv,20:00
Dagda,Sv

Rezekne,Riga,Tr,13:00,10.50
Dagda,Kraslava,  Ce,18:00,  2.50
Dagda,Kraslava,Ce,18:00,2.50,Sv
  Riga,Ventspils,  Pt,09:00  ,  6.70

Liepaja,Ventspils,Pt,17:00,5.50
 

Вывод:

 Riga Kraslava Pr 15:00 11.00
Riga Kraslava Pr 18:00 11.00
Kraslava Riga Pr 08:00 11.00
Kraslava Daugavpils Ot 10:00 3.00
Ventsplis 8.00 Liepaja Sv 20:00
Rezekne Riga Tr 13:00 10.50
Dagda Kraslava Ce 18:00 2.50
Dagda Kraslava Ce 18:00 2.50,Sv
Riga Ventspils Pt 09:00 6.70
Liepaja Ventspils Pt 17:00 5.50
*blank line*
 

Желаемый результат:

 Riga Kraslava Pr 15:00 11.00
Riga Kraslava Pr 18:00 11.00
Kraslava Riga Pr 08:00 11.00
Kraslava Daugavpils Ot 10:00 3.00
Ventsplis 8.00 Liepaja Sv 20:00
Rezekne Riga Tr 13:00 10.50
Dagda Kraslava Ce 18:00 2.50
Dagda Kraslava Ce 18:00 2.50,Sv
Riga Ventspils Pt 09:00 6.70
Liepaja Ventspils Pt 17:00 5.50
 

Комментарии:

1. И когда вы использовали свой отладчик для запуска своей программы, построчно, что ваш отладчик показал вам, что сделала ваша программа, когда достигнут конец ввода?

2. На последней итерации вам не нужно endl

3. Как ни странно, я получаю желаемый результат при запуске этой программы с этим вводом.

4. @TedLyngmo Может показаться, что это так, но перед тем, как «путь» (процесс 4128) завершился с кодом 0. Есть пустая строка.

5. @TedLyngmo Окей, спасибо. Я спрошу о проблеме моего профессора, может быть, тесты просто перепутались.

Ответ №1:

Вместо того, чтобы обнаруживать последнюю итерацию и избегать ввода новой строки, может быть проще, если вы добавите новую строку в начале цикла и, чтобы избежать постороннего перевода строки в начале вывода, сохраните логическую переменную is_first , изначально заданную в true и в начале вашего тела цикла

 if (!is_first) 
    cout << endl;
is_first = false;
 

Не красиво, но быстро кодируется.

Комментарии:

1. Вы имеете в виду «while (getline(file, line))» в начале этого while? Если это так, это тоже не сработало.

2. Да, это тот цикл, который я имел в виду — что неверно в выводе? Вы убедились, что определили ‘is_first’ перед этим циклом?

3. О, я не определил это перед циклом. Теперь я это сделал, теперь он отображает случайные интервалы между строками, но, похоже, в конце больше нет пустой строки. Я думаю, из одной проблемы в другую

4. Возможно, это должно быть внутри оператора if, который проверяет длину строки. В противном случае он будет печатать новую строку для каждой пустой строки во входных данных. Извините, я должен был прояснить эти моменты.

5. Все в порядке. Это улучшило его, но все еще не совсем исправлено. 😉

Ответ №2:

Я думаю, что этот подход более чистый:

 #include <iostream>
#include <fstream>
#include <algorithm>
#include <sstream>

using namespace std;

std::string filedata = R"(
Riga,Kraslava,Pr,15:00,11.00

Riga ,Kraslava,Pr ,18:00,11.00
   Kraslava,Riga,Pr,08:00,11.00
Kraslava,Daugavpils,Ot ,10:00, 3.00
Ventsplis,8.00,Liepaja,Sv,20:00
Dagda,Sv

Rezekne,Riga,Tr,13:00,10.50
Dagda,Kraslava,  Ce,18:00,  2.50
Dagda,Kraslava,Ce,18:00,2.50,Sv
  Riga,Ventspils,  Pt,09:00  ,  6.70

Liepaja,Ventspils,Pt,17:00,5.50

)";

std::string trim( const std::stringamp; str ) {
    std::size_t pos = str.find_first_not_of( " t" );
    if ( pos == std::string::npos ) return {};
    std::size_t last = str.find_last_not_of( " t" );
    if ( last == std::string::npos ) return {};
    return str.substr( pos, last - pos   1 );
}

std::vector<std::string> split( const std::stringamp; line ) {
    std::size_t pos = 0;
    std::size_t next = 0;
    std::vector<std::string> fields;
    while ( next != std::string::npos ) {
        next = line.find_first_of( ',', pos );
        std::string field = next == std::string::npos ? line.substr(pos) : line.substr(pos,next-pos);
        fields.push_back( trim( field ) );
        pos = next   1;
    }
    return fields;
}

int main() 
{
    istringstream file(filedata);
    cout << "result:" << endl;

    std::string line;
    while (getline(file, line)) {
        if (!line.empty()) {
            auto fields = split( line );
            for ( std::string field : fields ) {
                cout << field << ",";
            }
            cout << endl;
        }
    }
}
 

Приводит к

 result:
Riga,Kraslava,Pr,15:00,11.00,
Riga,Kraslava,Pr,18:00,11.00,
Kraslava,Riga,Pr,08:00,11.00,
Kraslava,Daugavpils,Ot,10:00,3.00,
Ventsplis,8.00,Liepaja,Sv,20:00,
Dagda,Sv,
Rezekne,Riga,Tr,13:00,10.50,
Dagda,Kraslava,Ce,18:00,2.50,
Dagda,Kraslava,Ce,18:00,2.50,Sv,
Riga,Ventspils,Pt,09:00,6.70,
Liepaja,Ventspils,Pt,17:00,5.50,
 

Compiler explorer: https://godbolt.org/z/enheGdjda

Комментарии:

1. Это действительно работает, но в этом коде есть гораздо больше, это лишь минимально воспроизводимый код. Я все еще хотел бы получить несколько предложений о том, как отредактировать код, который я должен получить, чтобы получить те же результаты с не таким огромным редактированием. Хотя, если больше ничего не появится, я думаю, мне придется отредактировать его по-своему.

2. Такие функции, как split() и trim(), явно отсутствуют в стандартной библиотеке C . Они все равно должны быть у вас. Они крайне необходимы для работы со строками.

3. @Jellyboy Существует по крайней мере std::views::split с C 20

4. Я знаю, но лично мне было неудобно использовать шаблон итератора. В большинстве случаев вы действительно хотите иметь все в одном месте, проверьте размер, выполните некоторую проверку с произвольным доступом.

5. Разбиение строки на токены / поля и сохранение результатов в векторе: все это с годами доступно на C . Это всегда однострочный. Нет необходимости создавать функцию или писать 10 строк кода. Поскольку вы очень опытный эксперт по программному обеспечению, вы скоро узнаете. Без обид. Это свободный мир. Каждый может делать, что хочет, поэтому, пожалуйста, продолжайте свой путь.

Ответ №3:

Исправление для этого — добавление

 line.erase(remove(line.begin(), line.end(), 'r'), line.end());
 

к кодексу. Но на всякий случай (это, вероятно, перебор) Я добавил это, как и при удалении интервала — эта строка для каждого значения (значение1, …, значение5). Это сразу устранило проблему. Из того, что я понял из Google, это предотвращает перемещение курсора в начале новой строки.

Комментарии:

1. Пожалуйста, добавьте объяснение вместе с вашим кодом для более полного и подробного ответа.