2010-06-28 3 views
12

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

Фон: В 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++, похоже, придерживаются устаревшей модели кодировки. Как бы вы разработали API Windows или инфраструктуру приложения, чтобы обойти эту проблему?

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

+2

извините, этот «вопрос» звучит как запись в блоге в маскировке ;-) – Philipp

+0

это может быть связано с моим вопросом: http://superuser.com/questions/157225/even-on-windows-7- can-you-do-a-dir-and-be-able-to-see-filenames-that-has-unico –

ответ

5

Общая стратегия, которую мы используем в большинстве (кросс-платформенных) приложениях/проектах: Мы просто используем UTF-8 (я имею в виду реальный стандарт) везде. Мы используем std :: string в качестве контейнера, и мы просто интерпретируем все как UTF8. И мы также обрабатываем весь файл IO таким образом, то есть ожидаем 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 и в некотором лог-файле. Посмотрите на the code, если вам интересно, как это делается.

4

Несколько точек:

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

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

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

+0

1. Microsoft предлагает проверить, идет ли стандартный поток вывода на консоль или что-то еще до использования WriteConsole. Это громоздко, но, по-видимому, это единственный возможный и портативный вариант. 2. Кодировки и кодировки не совпадают. С кодовыми страницами я имею в виду страницы консоли Windows. Поскольку консоль Windows ориентирована на текст и использует UTF-16, страницы кода устарели - каждая строка, использующая кодовую страницу, будет в любом случае конвертирована в UTF-16. Проблема «wostream» является неудачной, но обязана стандартом C++. 3. Я не думаю, что решение использовать UTF-16 неудобно ... – Philipp

+0

... ел, но API плохо разработан. Например, вы могли бы подумать о чем-то вроде «GetStdHandle (STD_UTF16LE_OUTPUT_HANDLE)», который вернет обработчик байт-ориентированного потока, который ожидает строки с кодировкой UTF-16-LE. Тогда вы можете использовать 'WriteFile' всюду. OTOH, я думаю, что проблема, что C и C++ не имеет реальных текстовых потоков, важнее. – Philipp

+0

Я думаю: «Весь мир, но windows api, использует потоки, ориентированные на байты для представления данных», немного завышает его. Java, C# и JavaScript также выполняют всю свою обработку символов и строк как текстовые потоки, UTF-16. – hippietrail

3

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

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

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

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

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

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

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

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

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

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

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

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

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

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

0

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

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