Это сильно пахнет проблемой текстового кодирования, поэтому я пошел вперед и попытался выполнить команду, которую вы предоставили, и, конечно же, выходной файл закодирован в UCS16LE. (Это 16-битные символы, little-endian.) Попробуйте открыть файл в шестнадцатеричном редакторе, чтобы увидеть, как он выглядит на самом деле.
Вы были на правильном пути, пытаясь использовать широкие строки, но иметь дело с Unicode может быть сложным. Следующие несколько параграфов дадут вам несколько советов о том, как с этим бороться, но если вам нужно быстрое и простое решение, переходите к концу.
Есть две вещи, которые нужно соблюдать. Во-первых, убедитесь, что вы также используете широкие потоки, например, wcout. Стоит лить каждого символа в int, чтобы дважды проверить, что нет проблемы с форматированием вывода.
Во-вторых, формат wcout, wstring и т. Д. Не является стандартным. В некоторых компиляторах это 2 байта на символ, а на других - 4. Обычно вы можете изменить это в своих настройках компилятора. C++ 11 также предоставляет std :: u16string и std :: u32string, которые более подробно описывают их размер.
Чтение текста в Юникоде, к сожалению, может быть довольно сложным с библиотекой C++, потому что даже если у вас есть нужный размер строки, вам нужно иметь дело с спецификациями и форматами endian, не говоря уже о канонизации.
Есть библиотеки, которые могут помочь с этим, но самым простым решением может быть просто открыть файл txt в «Блокноте», выбрать «Сохранить как», а затем выбрать более удобную для вас кодировку, такую как ANSI.
Редактировать: Если вас не устраивает быстрое и грязное решение, и вы не хотите использовать лучшую библиотеку Unicode, вы можете сделать это со стандартной библиотекой, но только если вы используете компилятор, который поддерживает C++ 11, например Visual Studio 2012.
В C++ 11 добавлены некоторые грани codecvt
для обработки преобразований между различными типами файлов Unicode. Это должно соответствовать вашей цели, но базовый дизайн этой части библиотеки был спроектирован в дни или годы, и их может быть довольно сложно понять. Держись за штаны.
Ниже строки, где вы открываете ifstream
, добавьте этот код:
infoFile.imbue(std::locale(infoFile.getloc(), new std::codecvt_utf16<char, 0x10FFFF, std::consume_header>));
Я знаю, что выглядит немного страшно. То, что он делает, - это сделать «локаль» из копии существующего языка, а затем добавить «грань» в локаль, которая обрабатывает преобразование формата.
«Локали» обрабатывают целую кучу вещей, в основном связанных с локализацией (например, как акцентировать валюту, например «100,00» против «100,00»). Каждое из правил в локали называется фасет. В стандартной библиотеке C++ кодирование файлов рассматривается как один из этих аспектов.
(Фон: ретроспективно, вероятно, было не очень мудрой идеей смешать кодировку файлов с локализацией, но в то время, когда эта часть библиотеки была разработана, кодирование файлов обычно диктовалось языком программы , так вот как мы попали в эту ситуацию.)
Таким образом, конструктор locale
берет копию по умолчанию locale
, созданный файловым потоком в качестве первого параметра, а вторым параметром является новый фасет.
codecvt_utf16
- это фасет для преобразования в и из utf-16. Первым параметром является «широкий» тип, то есть тип, используемый программой, а не тип, используемый в потоке байтов. Я указал здесь char
и работает с Visual Studio, но на самом деле он не действует в соответствии со стандартом. Я позабочусь об этом позже.
Второй параметр - это максимальное значение Unicode, которое вы хотите принять, не вызывая ошибки, и в обозримом будущем 0x10FFFF представляет собой самый большой символ Юникода.
Последний параметр - это битовая маска, которая изменяет поведение фасета.Я думал, что std::consume_header
будет особенно полезен для вас, так как wmic
выводит спецификацию (по крайней мере, на моей машине). Это будет потреблять эту спецификацию и выбрать, следует ли рассматривать ее как поток мало или big-endian в зависимости от того, что она получает.
Вы также заметите, что я создаю фасет в стеке с new
, но я не звоню delete
в любом месте. Это не очень безопасный способ создания библиотеки на современном C++, но, как я уже сказал, локали являются довольно старой частью библиотеки.
Будьте уверены, что вам не нужно delete
этот аспект. На самом деле это не очень хорошо документировано (поскольку локали настолько редко используются на практике), но фасет по умолчанию будет автоматически delete
d по языку, к которому он привязан.
Теперь, помните, как я сказал, что недействительно использовать char
как широкий тип? В стандарте говорится, что вы должны использовать whcar_t
, char16_t
или char32_t
, и если вы хотите поддерживать символы, отличные от ASCII, вы обязательно захотите это сделать. Самый простой способ сделать это в силе будет использовать wchar_t
, изменить ifstream
, string
, cout
и istringstream
к wifstream
, wstring
, wcout
и wistringstream
, то убедитесь, что постоянные ваши строки/полукокса имеют перед ними в L
, как так :
std::wcout << L"\nLine #" << lineNum << L":" << line << std::endl;
Все эти изменения необходимы, чтобы использовать широкие струны. Однако также будьте осторожны, что консоль Windows не может обрабатывать символы, отличные от ANSI, поэтому, если вы попытаетесь вывести такой символ (когда я запустил код, я попал в символ ™), поток wcout будет недействительным и перестанет выдавать что-либо. Если вы выводите файл, это не должно быть проблемой.
Возможно, вы можете сказать, что я не особенно волнуюсь об этой части стандартной библиотеки. На практике большинство людей, которые хотят использовать Unicode, будут использовать другую библиотеку (например, те, что я упомянул в комментариях), или перескакивают свои собственные кодеры/декодеры.
Имеет ли файл program.txt дополнительные пробелы при открытии его внутри редактора? Или сразу после того, как вы прочитали его в своей программе? –
Вы перенаправляетесь на 'product.txt', но читаете из' program.txt'. Это намеренно? – celtschk