Почему «новая строка» смещает положение байта всех символов в файле .txt +1?

Когда я использую fstream::tellg, после чтения первого символа с fstream::get (char) результат: 1

Затем я вставляю новую строку после первого символа

У меня fstream::seekg в начало: 0

Когда я использую fstream::tellg, на этот раз после прочтения первого символа результат: 2

Если я вставлю: "abc", в файл .txt:

  • после прочтения "a" tellg выдаст: 1
  • после "б" 2
  • и после "с" 3.

Но если я вставлю: "abc\n" или "abc" << endl;:

  • после прочтения "а" tellg выдаст 2
  • после "б" 3
  • после "c" 4
  • наконец 5 после новой строки.

Что является причиной этого?

Я понимаю, что «новая строка» - это тоже символы. Чего я не понимаю, так это смещения результата tellg после чтения символа. При каждом использовании «новой строки» это смещение увеличивается на единицу.

Обновлять

  • Заключение: Возникла проблема с настройкой моей IDE! Я использую Code :: Blocks. Я пробовал сборка программы в Microsoft Visual Studio IDE и он работал без каких-либо следов проблемы. Это не означает, что Code :: Blocks не работает. Возможно, это была проблема в моих настройках Code :: Blocks. Я не помню, чтобы что-то менял. Даже если бы это было так; Я, по моему скромному мнению, не считаю правильным то, что вы можете случайно изменить такие вещи. Я разочарован в Code :: Blocks.
  • мое решение: Изменить IDE

Зачем нужно это «обходить»? Символ новой строки - это реальный символ, который занимает один байт, как и любой другой символ. Его значение в ascii равно 0x0A. Вы также можете увидеть символы возврата каретки, которые также являются байтами, перед новой строкой, в зависимости от того, кто написал файл и из какой ОС вы его читаете. Ваш код должен этого ожидать. Чего вы ожидаете и как это мешает тому, что вы пытаетесь сделать?

Christopher Pisz 10.10.2018 23:17

Это может быть вопрос о данных написание текста vs запись двоичного кода, но это невозможно узнать, поскольку здесь не описывалась проблема.

Drew Dormann 10.10.2018 23:23

Почему бы вам не посмотреть байты, которые были записаны в файл? Откройте файл в шестнадцатеричной программе просмотра и посмотрите.

rustyx 10.10.2018 23:36

Какие функции вы используете для записи файла?

vll 11.10.2018 14:30

@rustyx Я использовал шестнадцатеричный просмотрщик, это было очень интересно, но я не нашел ничего странного; каждый персонаж был на своем месте. Однако я обнаружил одну вещь. Когда i fstream :: get в позиции 'новой строки' (\ r \ n), он увеличивает результат fstream :: tellg только на единицу. Возможно, поэтому все остальные символы смещаются при использовании fstream :: tellg. Но я надеюсь, что эта гипотеза неверна.

kevin kangaji 11.10.2018 14:32
Стоит ли изучать 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
5
729
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Я предполагаю, что вы пишете код в ОС Microsoft.

В текстовых файлах ОС Microsoft (и связанное программное обеспечение) ожидают, что конец строки будет отмечен последовательностью \r\n, поэтому, когда вы записываете новую строку в (текстовый) файл, он переводится с \n на \r\n. Таким образом, даже если вы вставили в поток только один символ, это привело к записи двух символов во внешний файл.

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

Верно, что когда вы имеете дело с текстовым файлом, tellg не дает очень значимого числа. У нас есть что-то вроде этого:

Верхняя часть - это данные, как вы их видите. Нижняя часть - это данные в том виде, в котором они хранятся в файле. Когда вы вызываете tellg, он сообщает вам позицию по нижней стороне, то есть позицию относительно начала файла. Но, в зависимости от того, сколько пар \ r \ n есть до этого в файле, это может привести к другому количеству символов в верхней строке, что вы увидите, когда прочитаете данные из файла.

Это означает, что результат от tellg можно использовать только несколькими довольно специфическими способами - в основном, когда вы получаете число от tellg, вы можете вернуть это число в seekg и начать чтение с того же места.

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

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string>

using namespace std;

std::string show(char x) {
    if (x > 32)
        return std::string(1, x);
    else switch (x) {
    case '\r': return "<\\r>";
    case '\n': return "<\\n>";
    case '\t': return "<\\t>";
    default: return "<BAD>";
    }
}

void display_txt_file(fstream& file)
{
    file.seekg(0, ios_base::beg);
    char x;
    cout << "tellg: " << file.tellg() << "| ";
    while (file.get(x))
    {
        cout << "'" << show(x) << "' tellg: " << file.tellg() << "| ";
    }
    file.clear();
    file.seekg(0, ios_base::end);
    std::cout << "\n";
//    cout << "\n> " << file.tellg() << "\n" << endl;
}

int main(int argc, char* argv[])
{
    ofstream new_file;
    new_file.open("test.txt");
    new_file.close();

    fstream file("test.txt", ios::in | ios::out);
    if (!file.is_open())
    {
        cout << "error file not opened" << endl;
        return 0;
    }

    file << "ABCD";
    display_txt_file(file);

    file.seekp(0);

    file << "ABCD\nE";
    display_txt_file(file);

    return 0;
}

Когда я запускаю это в Windows, я получаю следующий результат:

tellg: 0| 'A' tellg: 1| 'B' tellg: 2| 'C' tellg: 3| 'D' tellg: 4|
tellg: 0| 'A' tellg: 1| 'B' tellg: 2| 'C' tellg: 3| 'D' tellg: 4| '<\n>' tellg: 6| 'E' tellg: 7|

Итак, все, вплоть до новой строки, совпадает, как и следовало ожидать. Затем новая строка расширяется до двух символов, за которыми следует E. Но после того, как мы прочитали «А», tellg вернул 1, а не 2, как утверждалось в вопросе.

Открытие файла в двоичном режиме не приводит к исчезновению байтов. Это также совсем другое дело, чем чтение текста. Ваш ответ звучит так, как будто ios :: binary волшебным образом отменяет перевод строки возврата каретки в операционных системах Microsoft, что не относится к случаю или его использованию. Лучше OP действительно понимает, какие символы возврата каретки и новой строки находятся в текстовом режиме и что они занимают фактические байты, а также различия в кодировках символов и то, как fstream обрабатывает их по умолчанию.

Christopher Pisz 10.10.2018 23:22

Если кого-то волнует, как новая строка отображается в файле, а не возможность записать текстовый файл со строками так, как текстовые файлы должны быть отформатированы на этой платформе, то в значительной степени по определению он имеет дело с двоичным кодом. файл, и открытие в двоичном формате - (единственный) правильный способ справиться с ним.

Jerry Coffin 10.10.2018 23:26

Ваш комментарий неоднозначный. Кто такой «кто-то» и что им «небезразлично», и кто решил, что «ожидается» и что это за «определение»? Совершенно безответственно идти и говорить OP использовать двоичный режим без объяснения двоичного режима по сравнению с текстовым режимом. Я вообще ничего не вижу в сообщении ОП о написании. Я думаю, вы, должно быть, делаете ряд неверных предположений о том, что пытается сделать OP, и вы наверняка пропускаете несколько абзацев с объяснением текста и двоичных режимов, что такое кодировка символов и как fstream кодирует вещи по умолчанию.

Christopher Pisz 10.10.2018 23:30

Если бы я был новичком в C++, я бы прочитал ваш ответ, означающий: «Флаг ios :: binary удаляет символы новой строки в моем fstream», что абсолютно неверно.

Christopher Pisz 10.10.2018 23:34

@ChristopherPisz: Как могло: «когда вы пишете новую строку в (текстовый) файл, он переводится с \ n на \ r \ n. Чтобы этого не произошло, вы обычно хотите указать std :: ios :: двоичный режим при открытии файла ". возможно, это означает, что что-либо будет удалено из файла? Если вы вообще ничего не видите в вопросе OP о письме, вы, должно быть, вообще его не читали. Как «вставить» в: «Я затем вставляю« новую строку »...» означало бы что-нибудь, кроме записи?

Jerry Coffin 10.10.2018 23:39

Я понимаю, что «новая строка» также использует память, но вставка других символов не смещает каждый второй символ +1, почему я вижу это при вставке «новой строки»

kevin kangaji 11.10.2018 10:24

@JerryCoffin Спасибо, что нашли время. Я знаю, что такое двоичный режим. Чего я не понимаю, так это смещения всех результатов fstream :: tellg после использования \ r \ n. Я обнаружил, что когда я fstream :: get в \ r \ n, witch будет fstream :: get в последней позиции строки, fstream :: tellg только сообщает, что это позиция одного байта. Затем другой fstream :: get получает первый видимый символ строки.

kevin kangaji 11.10.2018 14:47

@kevinkangaji: Да, когда вы читаете \ r \ n в файле, он конвертируется и читается только как \ n в том, что вы читаете. fstream :: tellg просто сообщает вам, сколько байтов вы прошли от начала файла, что может не совпадать с количеством раз, которое вы читали из файла, чтобы добраться до этой точки.

Jerry Coffin 11.10.2018 16:00

@JerryCoffin: глядя на предоставленную вами иллюстрацию; "е" определенно смещено. Это 6-я вставленная, но 7-я по размеру. "a" в "b" не изменились на иллюстрации, потому что они появляются перед этим \ n преобразованием. Но я вижу это для каждого символа от первого до последнего символа, независимо от того, где вставлен \ n.

kevin kangaji 11.10.2018 17:32

@kevinkangaji: В этом случае, я думаю, нам нужно будет увидеть код, показывающий то, что вы видите - он не подходит ни к чему, что я когда-либо видел, ни к чему бы я ожидал увидеть.

Jerry Coffin 11.10.2018 17:35

@kevinkangaji: Вы используете комбинацию seekg (которая перемещает указатель "получить", т.е. позицию чтения) и tellp (которая сообщает вам указатель "положить" - позицию записи). Вы смотрите на два числа, которые совершенно не связаны между собой.

Jerry Coffin 12.10.2018 08:20

@JerryCoffin: По моему опыту, перемещение "положить" также перемещает "получить". Но я отвлекся, с моей стороны неуместно показывать код, который не соответствует моему вопросу, прошу прощения. Я отредактировал код, чтобы он был адекватным. Однако можно наблюдать тот же результат.

kevin kangaji 12.10.2018 15:40

@kevinkangaji: Я отредактировал ответ, включив в него версию вашего кода, слегка измененную, чтобы показать, что заявленного эффекта не происходит.

Jerry Coffin 12.10.2018 18:12

@JerryCoffin: Я запустил код на двух разных машинах и получил тот же результат, что и раньше. После «A» tellg возвращает 2 во втором проходе. Похоже, это связано с настройкой. Это не хорошо.

kevin kangaji 12.10.2018 19:02

@JerryCoffin: Я решил проблему, установив и используя Microsoft Visual Studio для запуска кода вместо Code :: Blocks, который я использовал до этого момента.

kevin kangaji 12.10.2018 20:05

В текстовом режиме tellg не обязательно представляет смещение файла (его единственное требование - его можно использовать для восстановления этой позиции с помощью seekg)

M.M 14.10.2018 00:16

Трудно сказать, что или почему вы будете работать над чем-либо, без объяснения ваших ожиданий и полного листинга кода.

Однако важно понимать кодировку символов при чтении и записи в файл.

Символ новой строки занимает байт. Его значение равно 0x0A, если мы используем набор символов ASCII. Помимо ASCII, существуют и другие кодировки символов. Также есть, например, кодировки UTF-8 или UTF-16. Каждая кодировка символов может иметь различное байтовое или многобайтовое представление для читаемого текстового символа, а также для нечитаемых текстовых символов, таких как новая строка.

В Windows существует соглашение об использовании возврата каретки с последующим переводом строки, а не просто переводом строки. Эти два байта будут выглядеть как 0x0D, 0x0A в ASCII. В системах * nix такого соглашения нет.

Следовательно, когда вы подсчитываете байты в своем fstream, вам нужно будет учитывать, что символ новой строки занимает байт или два байта, если вы ожидаете '\ r \ n', то есть если вы используете кодировку ASCII.

Насколько мне известно, fstream предполагает, что его контент - это ASCII. Это могло измениться с C++ 17. Думаю, были планы по поддержке различных кодировок символов в потоках. Те, кто находится на переднем крае, могли бы прокомментировать.

Ваша операционная система имеет кодировку символов по умолчанию, установленную где-то в ее конфигурации. Я знаю, что старые машины с Windows использовали Windows-1252. Я не уверен, что использует Windows 10. Я думаю, что большинство систем * nix используют UTF-8. В любом случае вы захотите проконсультироваться с конфигурацией вашей операционной системы.

Потоки C++ захотят преобразоваться из одного в другой, когда вы читаете и записываете в файл. Преобразование текста в его байтовое представление - большая часть того, что потоки пытаются сделать за вас.

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

Итак, имейте в виду, кто создал файл, как он выглядит как текст, каково его двоичное представление, в файле и в памяти, и код для него соответствующим образом.

К счастью для нас, некоторые кодировки также содержат весь набор символов ASCII и просто расширяют его. UTF-8 - одна из таких кодировок, которая делает это.

Вы можете обратиться к В чем разница между \ n и \ r \ n? для обсуждения этой темы.

Вы также можете обратиться к Разница между файлами, записанными в двоичном и текстовом режиме

"Стандартные потоки и локали IOS C++: Руководство и справочник для опытных программистов Книга Анжелики Лангер и Клауса Крефта - хорошая книга, если вы действительно хотите узнать свои стримы от и до.

Спасибо очень информативно. но все равно не понимаю; если я вставлю: "abc" в файл .txt; после прочтения «a» tellg выдаст: 1, после «b» 2 и после «c» 3. но если я вставлю: «abc \ n» или «abc» << endl; после прочтения «a» tellg выдаст 2, после «b» 3 и после «c» 4, наконец, 5 после новой строки

kevin kangaji 11.10.2018 14:49
Ответ принят как подходящий

Обновлять

  • Заключение: Произошла установка проблема с моей IDE! Я использовал Код :: Блоки. Я попытался собрать программу на Microsoft Visual Студия IDE, и она работала с никаких следов проблемы. Это делает не означает, что Code :: Blocks не работает. Это могло быть проблемой в мой Код :: Блокирует настройки. Я не помню, как менялся что-нибудь. Даже если бы это было так; Я, по моему скромному мнению, не думаю, что это правильно, что вы можете случайно изменить такие вещи. Я разочарован в Code :: Blocks.
  • мойРешение: изменить IDE

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

M.M 14.10.2018 00:17

Пожалуйста, опубликуйте минимальный воспроизводимый пример, небольшое приложение, демонстрирующее разницу.

rustyx 14.10.2018 11:22

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