Member 11593571 Ответов: 2

Как ввести пользовательский символ в файл с помощью fstream в C++


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

Что я уже пробовал:

вот фрагмент кода, с которым я открываю файл и вводю свой символ:
fstream write_on;
fstream Read_From;
const char *Write_File_Name = "C:\\users\\Username\\Desktop\\pic1.txt";
wchar_t const buf[] = L"ซ";

write_on.open(Write_File_Name, ios::binary | ios::out);
write_on.write(buf, 1);
write_on.close();


спасибо

Sergey Alexandrovich Kryukov

Ничто на самом деле не "преобразовано". Какая кодировка вам нужна? (Если это Юникод, пожалуйста, не отвечайте "Юникод". Какой UTF?) С Бом или нет? Если спецификации нет, некоторые программы, такие как текстовый редактор, могут "думать", что это ASCII/ANSI, даже если на самом деле это не так. Вам не нужно использовать спецификацию; вы просто не должны доверять каждой части программного обеспечения.

Кстати, зачем иметь символ в PNG-файле?

—СА

Member 11593571

ну, если вы думаете, что это как-то связано с текстовым редактором, то на этот раз я написал символ и прочитал его через свой код, текстовый редактор не нужен, Но все равно никакой разницы, и спасибо за ваш ответ

Sergey Alexandrovich Kryukov

Ладно, это нечто более определенное. Я могу заверить вас, что если вы запишете данные в файл и прочитаете его симметрично, то получите идентичные данные. К сожалению, вы не показали, как вы его читаете. Очень важно, чтобы вы не теряли никаких данных. Вы теряете его из-за размера 1. Это ошибка. См. Решение 1.

Вы должны понимать, что wchar_t зависит от реализации. В Windows, в частности, он ориентирован на символьное представление с использованием UTF-16L. Это означает, что один символ с кодовой точкой в BMP представлен в виде двух байтов, а другие символы используют пару 16-битных слов, называемых суррогатной парой. Итак, символ имел 2 или 4-байтовое представление; ваш случай, если 2 байта.

—СА

2 Ответов

Рейтинг:
8

Jochen Arndt

Я не думаю, что он преобразован в ASCII. Но вы вводите в файл только один байт (Нижний байт вашего широкого символа). Для записи всех символьных байтов используйте:

write_on.write(buf, sizeof(wchar_t));


[РЕДАКТИРОВАТЬ]
См. сомментс. Универсальным решением должно быть:
write_on.write((char*)buf, wcslen(buf) * sizeof(wchar_t));


В вашем примере строка " ซ " записывается в файл одним символом Юникода (тайский символ SO SO). Предполагая, что ваша платформа использует кодировку UTF-16LE для широких символов (например, Windows), кодовая точка Unicode равна 0x0E0B, а содержимое двоичного файла будет 0x0B, за которым последует 0x0E.

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

Если вы интерпретируете файл как ASCII (или какой-то 8-битный текст), вы будете читать управляющие символы ASCII 0x0B и 0x0E (VT и так далее). Но если вы интерпретируете его как UTF16-LE, вы получите кодовую точку 0x0E0B.
[/РЕДАКТИРОВАТЬ]


Sergey Alexandrovich Kryukov

Это, безусловно, так, но, строго говоря, не в 100% случаях, так что это почти правильно. Поэтому я проголосовал за ответ почти с 5: -).

Единственная недостающая часть-это упоминание о том, что wchar_t зависит от реализации и правильного объяснения этой "широкой" потери. В общем случае wchar_t не всегда является 16-битным, и массив не всегда будет иметь 1 элемент. Ваше решение верно при некоторых предположениях: например, когда подразумеваемая кодировка UTF-16, а wchar_t-16 бит, поэтому 16 бит, необходимых для представления этого конкретного символа, могут быть помещены в одно слово wchar_t.

Дополнительные пояснения см. в решении 2.

—СА

Jochen Arndt

Спасибо, Сергей. Вы правы, я должен был объяснить это более подробно.

Я использовал оператор sizeof (), чтобы учесть, что размер зависит от платформы. Таким образом, мой ответ верен во всех случаях для одного символа (как используется в вопросе). Для нескольких персонажей это конечно так

write_on. write(buf, wcslen(buf) * sizeof(wchar_t));

Sergey Alexandrovich Kryukov

Правильно. Вам лучше добавить этот фактор wcslen (buf) в ваше решение.
—СА

Member 11593571

в первую очередь без типажей баф на (типа char*), он не компилируется, так что я должен был сделать это так: write_on.писать (к(char*)buf с, wcslen(баф) * оператор sizeof(тип wchar_t)); но до сих пор не внес изменения и по-прежнему получать VT и поэтому вместо того, чтобы мой заказной характер.

Jochen Arndt

У вас есть широкая строка, состоящая из одного широкого символа. Предполагая, что ваша платформа использует UTF-16LE (как Windows), кодовая точка Unicode вашего символа равна 0x0E0B (тайский символ SO SO).

Это записывается в файл (он будет записывать значение, ориентированное на байт: 0x0B, за которым следует 0x0E).

Если вы теперь хотите прочитать файл, вы должны знать, как интерпретировать содержимое файла (как и любой файл, который Вы читаете). Если вы интерпретируете содержимое файла как символы ASCII, то увидите управляющие символы SO и VT. Но если вы интерпретируете содержимое как UTF-16LE, то получите символ 0x0E0B.

Поэтому, когда вы хотите вернуть своего персонажа, вы должны написать код, который выполняет обратную операцию:

- Получить размер файла
- Выделить тип wchar_t буфер (размер / оператор sizeof(тип wchar_t)) широкие символы (обычно добавляют один за завершающий нуль)
- Считывание содержимого файла в буфер (двоичный режим)
- Ваш персонаж(ы) вернется в выделенный буфер

Member 11593571

o. k я только что изменил 1 на sizeof(wchar_t), теперь он вводит на один символ больше, чем содержат сами буферные данные, прежде чем я имею в виду write_on. write(buf, 1); он преобразовывал мой пользовательский символ в (VT), но так, как вы сказали, как sizeof (wchar_t), он добавляет (SO) просто так, и именно так я читаю данные.
Read_From.открыть(Read_File_Name, а iOS::бинарные | ОС iOS::в); Read_From.читать(баф, оператор sizeof(буфер buf)); я также попытался Read_From.читать(буфер buf, 1);

Jochen Arndt

Нет никакого обращения.

Wchar_t представляет собой один символ, но содержит 2 или 4 байта в зависимости от реализации (см. ответ Сергея). Поэтому я выбрал sizeof(wchar_t), потому что это будет зависеть от реализации размера wchar_t в байтах.

Функция write записывает некоторое количество байтов из указанного буфера. Параметр buffer имеет тип char*. Но это не значит, что он пишет печатные символы. Он записывает байты.
Видишь http://www.cplusplus.com/reference/ostream/ostream/write/:
"Эта функция просто копирует блок данных, не проверяя его содержимое: Массив может содержать нулевые символы, которые также копируются без остановки процесса копирования."

Рейтинг:
2

Sergey Alexandrovich Kryukov

В дополнение к решению 1:

Пожалуйста, смотрите мой комментарий к этому вопросу.

Вы должны это понять wchar_t является зависеть от реализации В Windows, в частности, он ориентирован на представление символов с использованием UTF-16L, одной из кодировок Unicode. Формально говоря, это не обязательно должна быть какая-то конкретная кодировка; это могут быть просто какие-то произвольные данные заданного размера этого типа. Все зависит от того, как эти данные интерпретируются.

Это означает, что один символ с кодовой точкой в BMP представлен в виде двух байтов, а другие символы используют пару 16-битных слов, называемых суррогатной парой. Итак, символ имел 2 или 4-байтовое представление; ваш случай, если 2 байта, так что вам не нужен массив wchar_t, но в общем случае он вам понадобится. Затем вам нужно будет записать все элементы этого массива в свой файл и прочитать их соответствующим образом.

Смотреть также:
Широкий характер-Википедия, свободная энциклопедия,
BMP (Unicode) - Википедия, свободная энциклопедия,
"Дорожная карта" для БМП,
https://www.gnu.org/software/libunistring/manual/html_node/The-wchar_005ft-mess.html,
Часто задаваемые вопросы-UTF-8, UTF-16, UTF-32 & BOM[^].

—СА


Member 11593571

я прочитал все ссылки, которые вы дали мне выше, это было полезно, но не помогло мне решить мою проблему, так как вы сказали, что мой символ-это два байта, и wchar_t тоже может хранить два байта, тогда никаких проблем с тем, как я его ввожу, верно? и как я уже сказал выше, это, как я читал Read_From данных.открыть(Read_File_Name, а iOS::бинарные | ОС iOS::в); Read_From.читать(баф, оператор sizeof(тип wchar_t)); но не повезло там, я действительно не знаю, что я делаю не так, и что делать. но есть одна вещь, что если я использую один из символов ascii, он работает просто идеально.

Sergey Alexandrovich Kryukov

Как же так? Вы исправили свою ошибку с написанием 1?
—СА

Member 11593571

да, я изменил его на sizeof(wchar_t) или strlen(L"ซ"). я думаю, что это исправляет ошибку, но не устраняет мою проблему, так что у вас есть какие-нибудь идеи о том, как я могу написать ซ в своем текстовом файле?

Sergey Alexandrovich Kryukov

Или? Это две разные вещи. Извините, а какая именно строка после исправления? Какова ваша платформа?
—СА

Member 11593571

я использую windows

Sergey Alexandrovich Kryukov

Хорошо, тогда смотрите Решение 1. См. также комментарии к нему.
—СА

Member 11593571

я пробовал решения 1 и 2 снова и снова, но никаких изменений, и я прокомментировал Сергею Александровичу Крюкову ответ, если вы его прочтете.

Member 11593571

О боже, даже если я сделаю это просто cout < < "ซ"< & lt; endl; он печатает α ï ï. так что бы вы сказали сейчас?

Sergey Alexandrovich Kryukov

Вот в чем все дело: что значит"печатает"?
—СА

Andreas Gieriet

Во всей этой истории есть несколько составляющих:
1) ваш редактор: какую кодировку символов вы используете? Вы не можете доверять тому, что то, что вы видите, является тем, что получает компилятор. То есть попробуйте ввести символ в виде шестнадцатеричного кода L'\x..\x..' и т. д.
2) консоль, куда идет ваш printf: консоль получает последовательность байтов из вывода printf и должна иметь смысл в байтах. Убедитесь, что ваша консоль знает, как правильно отображать соответствующие последовательности байтов. например, моя консоль знает только 7-битный ASCII - другие символы отображаются как "мусор".
Овации
Энди

Sergey Alexandrovich Kryukov

Все дело в разделении интересов, исключении из уравнения всех несвязанных факторов. Каждый может сделать, например, круговую поездку, записать данные, прочитать их обратно и записать в другой файл, чтобы убедиться, что ничего не потеряно...
—СА

Sergey Alexandrovich Kryukov

Думаю, почти все знают. Я гарантирую, что если я что-то напишу, вы это прочтете.
Вы никогда не показывали свою дурацкую окончательную версию write с исправленной ошибкой и никогда не показывали, как Вы читаете.
—СА