Как вывести строки Unicode на консоли Windows - PullRequest
12 голосов
/ 28 июня 2010

уже есть несколько вопросов, касающихся этой проблемы. Я думаю, что мой вопрос немного отличается, потому что у меня нет реальной проблемы, я спрашиваю только из академического интереса. Я знаю, что реализация UTF-16 в Windows иногда противоречит стандарту Unicode (например, сопоставление) или ближе к старому UCS-2, чем к UTF-16, но я сохраню здесь терминологию «UTF-16» по причинам простота.

Справочная информация: в Windows все является UTF-16. Независимо от того, имеете ли вы дело с ядром, графической подсистемой, файловой системой или чем-то еще, вы передаете строки UTF-16. Здесь нет локалей или кодировок в смысле Unix. Для совместимости со средневековыми версиями Windows существует такая вещь, как «кодовые страницы», которая устарела, но тем не менее поддерживается. AFAIK, есть только одна правильная и не устаревшая функция для записи строк в консоль, а именно WriteConsoleW, которая принимает строку UTF-16. Кроме того, аналогичное обсуждение применимо к входным потокам, которые я тоже проигнорирую.

Тем не менее, я думаю, что это представляет недостаток дизайна в Windows API: есть универсальная функция, которая может использоваться для записи во все объекты потока (файлы, каналы, консоли ...), называемая WriteFile, но эта функция является байтовой -ориентирован и не принимает строки UTF-16. Документация предлагает использовать WriteConsoleW для консольного вывода, который ориентирован на текст, и WriteFile для всего остального, который ориентирован на байты. Поскольку и консольные потоки, и файловые объекты представлены дескрипторами ядра, а консольные потоки могут быть перенаправлены, необходимо вызывать функцию для каждой записи в стандартный поток вывода, которая проверяет, представляет ли дескриптор консольный поток или файл, нарушая полиморфность. OTOH, я действительно думаю, что разделение Windows между текстовыми строками и необработанными байтами (которое отражается во многих других системах, таких как Java или Python) концептуально превосходит Unix char* подход, который игнорирует кодировки и не различает строки и байтовые массивы.

Итак, мои вопросы: что делать в этой ситуации? И почему эта проблема не решается даже в собственных библиотеках Microsoft? Как библиотеки .NET Framework, так и библиотеки C и C ++ придерживаются устаревшей модели кодовых страниц. Как бы вы разработали Windows API или платформу приложений, чтобы обойти эту проблему?

Я думаю, что общая проблема (которую нелегко решить) состоит в том, что все библиотеки предполагают, что все потоки ориентированы на байты, и реализуют текстовые потоки поверх этого. Однако мы видим, что Windows имеет специальные текстовые потоки на уровне ОС, и библиотеки не могут справиться с этим. Поэтому в любом случае мы должны внести существенные изменения во все стандартные библиотеки. Быстрый и грязный способ состоит в том, чтобы рассматривать консоль как специальный байтовый поток, который принимает только одну кодировку. Для этого по-прежнему требуется обход стандартных библиотек C и C ++, поскольку они не реализуют переключатель WriteFile / WriteConsoleW. Это правильно?

Ответы [ 4 ]

5 голосов
/ 19 июля 2010

Общая стратегия, которую я / мы используем в большинстве (кроссплатформенных) приложений / проектов: мы просто везде используем UTF-8 (я имею в виду настоящий стандарт).Мы используем std :: string в качестве контейнера и просто интерпретируем everything как UTF8.И мы также обрабатываем все операции ввода-вывода файлов, то есть ожидаем UTF8 и сохраняем UTF8.В случае, когда мы получаем строку откуда-то и знаем, что это не UTF8, мы конвертируем ее в UTF8.

Наиболее распространенный случай, когда мы наталкиваемся на WinUTF16, касается имен файлов.Поэтому для каждой обработки имени файла мы всегда конвертируем строку UTF8 в WinUTF16.А также другой способ, если мы ищем в каталоге файлы.

Консоль в действительности не используется в нашей сборке Windows (в сборке Windows все выходные данные консоли упакованы в файл).Поскольку у нас везде есть UTF8, также наш консольный вывод - UTF8, что хорошо для большинства современных систем.Кроме того, файл журнала консоли Windows имеет свое содержимое в UTF8, и большинство текстовых редакторов в Windows могут прочитать это без проблем.

Если бы мы использовали WinConsole больше и если бы нам было важно, чтобы все специальные символы былиотображается правильно, возможно, мы напишем какой-нибудь автоматический обработчик канала, который мы устанавливаем между fileno=0 и действительным stdout, который будет использовать WriteConsoleW, как вы предложили (если на самом деле нет более простого способа).

Если вам интересно, как реализовать такой автоматический обработчик канала: мы уже реализовали это для всех систем, подобных POSIX.Код, вероятно, не работает в Windows, как он есть, но я думаю, что должно быть возможно его портировать.Наш текущий обработчик канала похож на то, что делает tee.Т.е. если вы сделаете cout << "Hello" << endl, он будет напечатан как на stdout, так и в каком-нибудь лог-файле.Посмотрите на код , если вам интересно, как это делается.

4 голосов
/ 17 августа 2010

Несколько моментов:

  1. Одно важное различие между Windows "WriteConsoleW" и printf заключается в том, что WriteConsoleW смотрит на консоль как на графический интерфейс пользователя, а не на их текстовый поток.Например, если вы используете его и используете pipe, вы не будете захватывать вывод.
  2. Я бы никогда не сказал, что кодовые страницы устарели.Возможно, разработчики Windows хотели бы, чтобы они были такими, но они никогда не будут.Весь мир, кроме Windows API, использует байтово-ориентированные потоки для представления данных: XML, HTML, HTTP, Unix и т. Д., И т. Д. Используют кодировки, и наиболее популярным и мощным является UTF-8.Таким образом, вы можете использовать широкие строки для внутреннего использования, но во внешнем мире вам понадобится что-то еще.

    Даже когда вы печатаете wcout << L"Hello World" << endl, он преобразуется изнутри в поток байтов, в большинстве систем (кроме окон)UTF-8.

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

3 голосов
/ 13 августа 2011

Чтобы ответить на ваш первый вопрос, вы можете вывести строки Unicode в консоль Windows, используя _setmode . Конкретные подробности относительно этого можно найти в блоге Майкла Каплана . По умолчанию консоль не является Unicode (UCS-2 / UTF-16). Он работает в режиме Ansi (локаль / кодовая страница) и должен быть специально настроен для использования Unicode.

Кроме того, вы должны изменить шрифт консоли, так как шрифт по умолчанию поддерживает только символы Ansi. Здесь есть некоторые незначительные исключения, такие как символы ASCII с расширением нуля, но для печати реальных символов Unicode необходимо использовать _setmode.

В Windows все является UTF-16. Независимо от того, имеете ли вы дело с ядром, графической подсистемой, файловой системой или чем-то еще, вы передаете строки UTF-16. В смысле Unix нет локалей или кодировок.

Это не совсем так. Хотя базовое ядро ​​Windows действительно использует Unicode, в игру вступает огромное количество возможностей взаимодействия, которые позволяют Windows взаимодействовать с широким спектром программного обеспечения.

Рассмотрим блокнот (да, блокнот далек от основного компонента, но он меня понял). Блокнот имеет возможность читать файлы, которые содержат Ansi (вашу текущую кодовую страницу), Unicode или UTF-8. Вы можете считать блокнот приложением Unicode, но это не совсем точно.

Лучший пример - драйверы. Драйверы могут быть написаны на Unicode или Ansi. Это действительно зависит от характера интерфейса. Для этого Microsoft предоставляет библиотеку StrSafe , которая была специально написана с учетом драйверов режима ядра и включает в себя как Unicode, так и Ansi версии . Хотя драйверы являются либо Ansi, либо Unicode, ядро ​​Windows должно взаимодействовать с ними - правильно - независимо от того, какую форму они принимают.

Чем дальше вы находитесь от ядра Windows, тем больше взаимодействия вступает в игру. Это включает кодовые страницы и локали . Вы должны помнить, что не все программное обеспечение написано с учетом Unicode. Visual C ++ 2010 по-прежнему обладает способностью строить с использованием Ansi, Multi-Byte или Unicode. Это включает использование кодовых страниц и locales , которые являются частью стандарта C / C ++.

Однако, я думаю, что это представляет собой недостаток дизайна в Windows API

следующие две статьи обсуждают это довольно хорошо.

Итак, мои вопросы: что делать в этой ситуации? И почему эта проблема не решается даже в собственных библиотеках Microsoft? Как библиотеки .NET Framework, так и библиотеки C и C ++ придерживаются устаревшей модели кодовых страниц. Как бы вы разработали Windows API или прикладную среду, чтобы обойти эту проблему?

На данный момент, я думаю, вы смотрите на Windows в ретроспективно . Unicode не пришел первым, ASCII сделал. После ASCII пришли кодовые страницы . После кодовых страниц пришли DBCS . После DBCS пришли MBCS (и в конечном итоге UTF-8). После UTF-8 пришел Unicode (UTF-16 / UCS-2).

Каждая из этих технологий была включена в ОС Windows на протяжении многих лет. Каждое здание на последнем, но не ломая друг друга. Программное обеспечение было написано с учетом каждого из них. Иногда это может показаться не таким, но Microsoft прилагает огромное количество усилий к , а не программам, которые она не писала. Даже сейчас вы можете написать новое программное обеспечение, которое использует любую из этих технологий, и оно будет работать.

Реальный ответ здесь - «совместимость». Microsoft по-прежнему использует эти технологии, как и многие другие компании. Существует огромное количество программ, компонентов и библиотек, которые не были обновлены (или будут обновлены) для использования Unicode. Даже когда появляются новые технологии, такие как .NET, старые технологии должны оставаться на месте. По крайней мере, для совместимости.

Например, допустим, у вас есть DLL, с которой вам нужно взаимодействовать из .NET, но эта DLL была написана с использованием Ansi (локализованная однобайтовая кодовая страница) Что еще хуже, у вас нет источника для DLL. Единственный ответ здесь - использовать эти устаревшие функции.

0 голосов
/ 10 июля 2015

Как я корректирую работу следующим образом:

  • Используйте UTF-16 и wchar_t для внутреннего использования, это прекрасно работает с именами файлов и Windows API в целом.
  • Установите кодовую страницу 65001, которая является UTF-8. Это гарантирует, что при чтении текстовых файлов Windows проверяет их на наличие UTF-16 и спецификации («стандарт Windows»), а при отсутствии спецификации текст будет обрабатываться как UTF-8 («мировой стандарт») и переводиться в UTF-16 для вашего использования.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...