Когда я использую fstream::tellg
, после чтения первого символа с fstream::get (char)
результат: 1
Затем я вставляю новую строку после первого символа
У меня fstream::seekg
в начало: 0
Когда я использую fstream::tellg
, на этот раз после прочтения первого символа
результат: 2
Если я вставлю: "abc"
, в файл .txt:
tellg
выдаст: 1Но если я вставлю: "abc\n"
или "abc" << endl;
:
tellg
выдаст 2Что является причиной этого?
Я понимаю, что «новая строка» - это тоже символы. Чего я не понимаю, так это смещения результата tellg
после чтения символа. При каждом использовании «новой строки» это смещение увеличивается на единицу.
Это может быть вопрос о данных написание текста vs запись двоичного кода, но это невозможно узнать, поскольку здесь не описывалась проблема.
Почему бы вам не посмотреть байты, которые были записаны в файл? Откройте файл в шестнадцатеричной программе просмотра и посмотрите.
Какие функции вы используете для записи файла?
@rustyx Я использовал шестнадцатеричный просмотрщик, это было очень интересно, но я не нашел ничего странного; каждый персонаж был на своем месте. Однако я обнаружил одну вещь. Когда i fstream :: get в позиции 'новой строки' (\ r \ n), он увеличивает результат fstream :: tellg только на единицу. Возможно, поэтому все остальные символы смещаются при использовании fstream :: tellg. Но я надеюсь, что эта гипотеза неверна.
Я предполагаю, что вы пишете код в ОС 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 обрабатывает их по умолчанию.
Если кого-то волнует, как новая строка отображается в файле, а не возможность записать текстовый файл со строками так, как текстовые файлы должны быть отформатированы на этой платформе, то в значительной степени по определению он имеет дело с двоичным кодом. файл, и открытие в двоичном формате - (единственный) правильный способ справиться с ним.
Ваш комментарий неоднозначный. Кто такой «кто-то» и что им «небезразлично», и кто решил, что «ожидается» и что это за «определение»? Совершенно безответственно идти и говорить OP использовать двоичный режим без объяснения двоичного режима по сравнению с текстовым режимом. Я вообще ничего не вижу в сообщении ОП о написании. Я думаю, вы, должно быть, делаете ряд неверных предположений о том, что пытается сделать OP, и вы наверняка пропускаете несколько абзацев с объяснением текста и двоичных режимов, что такое кодировка символов и как fstream кодирует вещи по умолчанию.
Если бы я был новичком в C++, я бы прочитал ваш ответ, означающий: «Флаг ios :: binary удаляет символы новой строки в моем fstream», что абсолютно неверно.
@ChristopherPisz: Как могло: «когда вы пишете новую строку в (текстовый) файл, он переводится с \ n на \ r \ n. Чтобы этого не произошло, вы обычно хотите указать std :: ios :: двоичный режим при открытии файла ". возможно, это означает, что что-либо будет удалено из файла? Если вы вообще ничего не видите в вопросе OP о письме, вы, должно быть, вообще его не читали. Как «вставить» в: «Я затем вставляю« новую строку »...» означало бы что-нибудь, кроме записи?
Я понимаю, что «новая строка» также использует память, но вставка других символов не смещает каждый второй символ +1, почему я вижу это при вставке «новой строки»
@JerryCoffin Спасибо, что нашли время. Я знаю, что такое двоичный режим. Чего я не понимаю, так это смещения всех результатов fstream :: tellg после использования \ r \ n. Я обнаружил, что когда я fstream :: get в \ r \ n, witch будет fstream :: get в последней позиции строки, fstream :: tellg только сообщает, что это позиция одного байта. Затем другой fstream :: get получает первый видимый символ строки.
@kevinkangaji: Да, когда вы читаете \ r \ n в файле, он конвертируется и читается только как \ n в том, что вы читаете. fstream :: tellg просто сообщает вам, сколько байтов вы прошли от начала файла, что может не совпадать с количеством раз, которое вы читали из файла, чтобы добраться до этой точки.
@JerryCoffin: глядя на предоставленную вами иллюстрацию; "е" определенно смещено. Это 6-я вставленная, но 7-я по размеру. "a" в "b" не изменились на иллюстрации, потому что они появляются перед этим \ n преобразованием. Но я вижу это для каждого символа от первого до последнего символа, независимо от того, где вставлен \ n.
@kevinkangaji: В этом случае, я думаю, нам нужно будет увидеть код, показывающий то, что вы видите - он не подходит ни к чему, что я когда-либо видел, ни к чему бы я ожидал увидеть.
@kevinkangaji: Вы используете комбинацию seekg
(которая перемещает указатель "получить", т.е. позицию чтения) и tellp
(которая сообщает вам указатель "положить" - позицию записи). Вы смотрите на два числа, которые совершенно не связаны между собой.
@JerryCoffin: По моему опыту, перемещение "положить" также перемещает "получить". Но я отвлекся, с моей стороны неуместно показывать код, который не соответствует моему вопросу, прошу прощения. Я отредактировал код, чтобы он был адекватным. Однако можно наблюдать тот же результат.
@kevinkangaji: Я отредактировал ответ, включив в него версию вашего кода, слегка измененную, чтобы показать, что заявленного эффекта не происходит.
@JerryCoffin: Я запустил код на двух разных машинах и получил тот же результат, что и раньше. После «A» tellg возвращает 2 во втором проходе. Похоже, это связано с настройкой. Это не хорошо.
@JerryCoffin: Я решил проблему, установив и используя Microsoft Visual Studio для запуска кода вместо Code :: Blocks, который я использовал до этого момента.
В текстовом режиме tellg
не обязательно представляет смещение файла (его единственное требование - его можно использовать для восстановления этой позиции с помощью seekg
)
Трудно сказать, что или почему вы будете работать над чем-либо, без объяснения ваших ожиданий и полного листинга кода.
Однако важно понимать кодировку символов при чтении и записи в файл.
Символ новой строки занимает байт. Его значение равно 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 после новой строки
Это, вероятно, означает, что у вас есть ошибка в вашей программе, которая работает так, как вы ожидаете, в MSVC.
Пожалуйста, опубликуйте минимальный воспроизводимый пример, небольшое приложение, демонстрирующее разницу.
Зачем нужно это «обходить»? Символ новой строки - это реальный символ, который занимает один байт, как и любой другой символ. Его значение в ascii равно 0x0A. Вы также можете увидеть символы возврата каретки, которые также являются байтами, перед новой строкой, в зависимости от того, кто написал файл и из какой ОС вы его читаете. Ваш код должен этого ожидать. Чего вы ожидаете и как это мешает тому, что вы пытаетесь сделать?