Ifstream чтение дополнительных символов (CR 13 LF 10) в конце строк

В настоящее время я читаю текстовый файл, в котором новые строки занимают 2 байта, поскольку он записывает новую строку как CRLF, а не только LF.

std::fstream fileReader = std::fstream(_filename, std::ios::in);

// READ THE SIZE OF THE FILE:
fileReader.seekg(0, fileReader.end); // set reader position to the end
std::streamsize fileSize = fileReader.tellg(); // get reader position
fileReader.seekg(0, fileReader.beg); // set reader position to the start

// SET UP THE BUFFER:
std::vector<char> buffer; buffer.resize(fileSize, '\0');
buffer.back() = '\0';

// READ:
fileReader.read(buffer.data(), fileSize);

Проблема в том, что «fileSize» на самом деле является размером файла, а не количеством символов, которые не являются CF в файле, чего он ожидает.

Есть ли способ получить этот номер автоматически?

В противном случае, я полагаю, что двоичный режим - единственный оставшийся вариант, хотя это было бы довольно разочаровывающим, поскольку я ожидал правильного автоматического форматирования, когда не использовал двоичный режим. Кроме того, функция .read дает сбой (failReader имеет значение true).

Вы хотите, чтобы он писал только LF?

tadman 14.12.2020 03:37

@tadman Это не письмо, это чтение. Я мог бы изменить файл, чтобы удалить CF без проблем, я просто разочарован тем, что он не работает автоматически - ifstream четко знает, что есть символы CF, поскольку он удаляет их, и все же ожидает, что я дам сумму символов, отличных от CF, вместо общего количества символов. Я чувствую, что мне не нужно «исправлять файлы», чтобы ifstream работал.

ZeroZ30o 14.12.2020 03:39

CF означает последовательность «CRLF»? Ожидается, что размер файла будет включать любые разделители строк. Если вам нужен «чистый» размер за вычетом этого, вам придется прочитать и отфильтровать, чтобы узнать. Операционная система не предоставляет эту информацию, поэтому C++ не может ничего узнать, кроме как вычислить. Не уверен, как эта разница здесь актуальна, поскольку вам нужен весь файл в буфере, поэтому буфер должен быть достаточно большим, чтобы вместить это.

tadman 14.12.2020 03:40

Следует учитывать одну вещь: вместо использования необработанного буфера символов просто выгружайте его в std::string.

tadman 14.12.2020 03:42

@tadman ifstream имеет флаг для чтения двоичных файлов, если этот флаг не установлен, он предназначен для автоматического анализа текстовых файлов. Опять же, при использовании .read в буфере он удаляет символы CF, поэтому явно выполняется какая-то работа по их удалению. Тем не менее, это вызывает раздражение, если я дополнительно не даю ему этот номер. Я просто спрашиваю, может быть, поскольку он явно способен обнаруживать эти символы CF, мне не хватает решения?

ZeroZ30o 14.12.2020 03:43

Ему нужно знать, насколько велик целевой буфер, чтобы он не превышал границы. Это сделано для предотвращения ошибок переполнения буфера. То, что вы перераспределяете на небольшую сумму, обычно не имеет большого значения. Накладные расходы незначительны, и помните, что в std::vector уже встроен некоторый резерв.

tadman 14.12.2020 03:44

Он может отображать их при чтении, но не может случайно пропустить файл, используя seekg и тому подобное. Вычисление фактической длины требует больших затрат, поэтому C++ не делает этого, если вы явно не попросите.

tadman 14.12.2020 03:45

@tadman Это неверно и не имеет значения, поскольку я указываю как размер буфера, так и количество символов для чтения. Переполнения буфера не происходит. Происходит то, что бит ошибки fstream устанавливается, когда я указываю истинный размер файла, но не если я указываю (размер файла минус количество строк). Ожидается КОЛИЧЕСТВО СИМВОЛОВ НЕ-CF, несмотря на выполнение внутренней работы по фильтрации символов CF (он не копирует их в буфер).

ZeroZ30o 14.12.2020 03:47

@tadman Да, именно это меня так беспокоит, так это то, что Tellg предоставляет мне правильный размер файла в самом конце, и все же по какой-то причине это сбивает с толку. Если я не даю вызов чтения (размер файла - количество строк), это ошибка. Когда это ошибка, если я проверю, весь файл был прочитан. Тем не менее, он удалил все символы CF - ожидая, что я замечу, что они отсутствуют ... Я только сейчас понял, что это выбор реализации, так что размер буфера равен количеству записанных в него символов ... черт, я разочарован в кто бы это ни решил, файлы должны быть простыми.

ZeroZ30o 14.12.2020 03:51

Вы читаете файл в Windows? Или какая-то другая ОС?

Eljay 14.12.2020 03:52

«Это то, что он ожидает». -- Что такое "это" в этой фразе? Если я нарисую это предложение, предшествующее значение будет «fileSize», но это не имеет смысла.

JaMiT 14.12.2020 03:56

@Eljay читает файл в Windows, который согласно этому stackoverflow.com/questions/44645395/… соответствует правильному форматированию

ZeroZ30o 14.12.2020 03:57

@JaMiT «это то, что он ожидает» относится к последней сумме, которую я написал, поэтому «количество символов, которые не являются CF», но я вижу, как это сбивает с толку. Лучшей формулировкой было бы: он ожидает, что я дам ему «количество символов, которые будут извлечены в буфер». Моя проблема в том, что я не могу предсказать, сколько символов CF он будет игнорировать, не проанализировав его самостоятельно, что в первую очередь противоречит всей цели работы .read в недвоичном режиме.

ZeroZ30o 14.12.2020 03:59

@ZeroZ30o А? Вы говорите, что я мог бы переписать это предложение так: «Проблема в том, что 'fileSize' на самом деле является размером файла, а не количеством символов, которые не являются CF в файле, что и является тем, что [ ожидаемое количество символов, не являющихся CF]. Я попросил антецедент «это», а не антецедент «который». Та же проблема существует и в вашей «лучшей формулировке» — что ожидает [X]?

JaMiT 14.12.2020 04:01

@JaMiT Я сбит с толку тем, что ты только что сказал. Я говорю, что он ошибается, потому что ожидает, что я укажу количество символов, которые будут извлечены. Он не ожидает, что я укажу размер файла, хотя это явно то, что он использует внутри, чтобы знать, когда остановиться. Он также четко знает, сколько символов CF он пропустил, поэтому он может легко указать количество извлеченных символов и позволить мне указать размер файла. Я просто говорю, что кому-то на Земле было бы трудно предложить реализацию более раздражающую, чем эта.

ZeroZ30o 14.12.2020 04:10

Если вы переключитесь на Linux, у вас не будет этой проблемы, где конец строки - это просто NLs, поэтому получение размера файла таким образом (или через stat) фактически дает вам точное количество символов, которое вы сможете читать с начала файла до его конца. Единственный другой вариант — открыть файл с флагом ios::binary, но это означает, что вы увидите CRs в файле, когда будете его читать, и вам придется с ними разбираться самостоятельно.

Sam Varshavchik 14.12.2020 04:10

@SamVarshavchik Да, я знаю о Linux - они делают это логично, им спасибо. И да, я пришел к выводу, что единственный выбор - использовать ios:binary, просто глупо, что они даже позволяют людям использовать .read без бинарного флага, явно спецификация не была продумана.

ZeroZ30o 14.12.2020 04:16

Есть ли способ получить этот номер автоматически? Нет, увы. Windows использует CRLF, который она получила от DOS, который DOS получил от CP/M. Построчное чтение преобразует CRLF в конец строки, но массовое чтение данных не сработает (если я правильно помню, я давно использую двоичный код для чтения/записи в Windows).

Eljay 14.12.2020 04:20

@ ZeroZ30o «Я говорю, что это ошибка» - это имеет такое же отсутствие смысла, как и ваши предыдущие утверждения. "Оно", "оно", "оно" без указания, что это такое. Что это за так называемое "оно" с ошибками? Чего ожидать-то?

JaMiT 14.12.2020 04:23

@JaMiT Ошибки вызова .read, что еще может быть ошибкой? Это единственное, что называется ошибкой CAN, так как размер буфера изменяется до соответствующего размера. Если вы хотите быть мелочным: «ссылка ifstream, возвращаемая вызовом .read, при преобразовании в логическое значение возвращает false - установлен ее бит ошибки». И это последнее «свое» относится к ссылке ifstream. Это единственное, что имеет failbit.

ZeroZ30o 14.12.2020 04:25

@ ZeroZ30o Хорошо, так что вызов read также является тем, что, как вы утверждаете, ожидает «количества символов, которые не являются CF в файле»? (Потому что вызов read этого не ожидает. Небольшое превышение допустимо.)

JaMiT 14.12.2020 04:30

@JaMiT Да, это то, что я утверждаю. А еще, ложный. Переход только на 1 вызывает ошибку. То есть, если я скажу ему извлечь 10 символов (поскольку размер файла, который он мне дал, был 10), но он извлечет только 9 (поскольку он нашел и проигнорировал символ CF) и достигнет конца файла, файловый поток завершится ошибкой.

ZeroZ30o 14.12.2020 04:33
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
22
390
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Это нормальное поведение:

ifstream::read ожидает в качестве параметра "количество символов, которые будут извлечены", а не размер файла.

Так как это количество невозможно предсказать, и функция также не предоставляет его, использование ifstream::read без флага ios::binary совершенно бесполезно, если только файл НЕ ИЗВЕСТЕН, что он не содержит символов CF, которые заставят ifstream сходить с ума. .

(Я не знаю, заставляют ли Ифстрима нервничать и другие персонажи)

Я предлагаю использовать ios::binary в fstream даже для чтения текстовых файлов, чтобы прочитать их за одну операцию. Вероятно, и для записи файлов (особенно если вы работаете в Windows).

Есть ли способ получить этот номер автоматически?

Это не может быть сделано автоматически, так как файловая система не хранит количество окончаний строк в файле. Любой подход должен будет пройти через файл, проверяя каждый символ. К счастью, можно использовать класс std::fstream для выполнения большей части черновой работы. Полученная функция удивительно похожа на то, что у вас есть сейчас. Вам просто нужно захватить количество прочитанных символов.

// Gets the number of characters in `textfile` accounting for CR-LF being read as one character.
// The stream will be reset to the beginning when this function returns.
std::streamsize char_count(std::fstream & textfile)
{
    std::streamsize count = textfile.gcount();

    // Get an upper bound on the size.
    textfile.clear();
    textfile.seekg(0, textfile.end); // set reader position to the end
    std::streamsize fileSize = textfile.tellg(); // get reader position

    if ( textfile  &&  fileSize != -1 )
    {
        // Read the text.
        std::vector<char> buffer(fileSize);
        textfile.seekg(0, textfile.beg); // set reader position to the start
        textfile.read(buffer.data(), fileSize);

        // Get the count of characters read.
        count = textfile.gcount();
    }

    // Reset the stream.
    textfile.clear(); // Because over-reading would set some flags.
    textfile.seekg(0, textfile.beg);
    textfile.clear(); // In case the seek failed. We did promise to reset the stream.

    return count;
}

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

gcount() возвращает «количество символов, извлеченных последней операцией неформатированного ввода, выполненной над объектом», согласно cplusplus.com. Я не проверял, но подозреваю, что из-за этого работать не будет. Потому что это будет отформатированная операция (если вы не используете ios::binary, в этом случае нет смысла делать что-либо из этого).

ZeroZ30o 14.12.2020 04:37

@ ZeroZ30o Подозревай, что хочешь. Сохраняйте свое непонимание потоков, если хотите. Этот ответ является временной мерой. Вам было бы лучше спросить о «шипении» fstream (ваша реальная проблема), вместо того, чтобы спрашивать о том, что, по вашему мнению, решит ситуацию (ваша проблема XY — прочитайте статью по ссылке, если вы не знакомы со сроком).

JaMiT 15.12.2020 01:52

Если вы используете std::ifstream::binary для чтения файла, он будет считывать каждый байт в память. Я думаю, это довольно разумно.

Если вы не используете std::ifstream::binary, ifstream преобразует CRLF в LF, что делает длину строки (strlen и т. д.) отличной от размера файла (tellg()). Это правило было довольно незаметным...

Другие вопросы по теме