Почему «новая строка» смещает позицию байта всех символов в текстовом файле +1? - PullRequest
0 голосов
/ 11 октября 2018

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

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

Ifstream::seekg в начало: 0

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

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

  • после прочтения «a» tellg даст: 1
  • после «b» 2
  • и после «c» 3.

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

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

В чем причина?

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

Обновление

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

Ответы [ 3 ]

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

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

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

Символ новой строки занимает байт.Это значение 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 ++: Расширенное руководство для программиста и справочник Анджелики Лангер и Клауса Крефта" - хорошая книга, если вы действительно хотите узнать своипотоки внутри и снаружи.

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

Обновление

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

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

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

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

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

enter image description here

Верхняя часть - это данные, которые вы видите.Нижняя сторона - это данные, которые хранятся в файле.Когда вы вызываете 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, как было заявлено в вопросе.

...