Какую кодировку / кодовую страницу использует cmd.exe?

271

Когда я открываю cmd.exe в Windows, какую кодировку он использует?

Как я могу проверить, какая кодировка используется в настоящее время? Зависит ли это от моих региональных настроек или есть какие-то переменные среды для проверки?

Что происходит при вводе файла с определенной кодировкой? Иногда я получаю искаженные символы (используется неправильная кодировка), а иногда это работает. Однако я ничему не доверяю, пока не знаю, что происходит. Кто-нибудь может объяснить?

danglund
источник

Ответы:

389

Да, это расстраивает - иногда typeи другие программы печатают бред, а иногда нет.

Прежде всего, символы Unicode будут отображаться только в том случае, если текущий шрифт консоли содержит символы . Поэтому используйте шрифт TrueType, например Lucida Console, вместо стандартного шрифта Raster.

Но если консольный шрифт не содержит символа, который вы пытаетесь отобразить, вместо тарабарщины вы увидите вопросительные знаки. Когда вы получаете тарабарщину, происходит больше, чем просто настройки шрифта.

Когда программы используют стандартные функции ввода-вывода C-библиотеки, например printf, выходная кодировка программы должна совпадать с выходной кодировкой консоли , иначе вы получите бред. chcpпоказывает и устанавливает текущую кодовую страницу. Весь вывод, использующий стандартные функции ввода-вывода библиотеки C, обрабатывается так, как если бы он находился в отображаемой кодовой странице chcp.

Сопоставление выходной кодировки программы с выходной кодировкой консоли можно выполнить двумя различными способами:

  • Программа может получить текущую кодовую страницу консоли, используя chcpили GetConsoleOutputCP, и сконфигурировать себя для вывода в этой кодировке, или

  • Вы или программа можете установить текущую кодовую страницу консоли с помощью chcpили SetConsoleOutputCPсоответствовать выходной кодировке программы по умолчанию.

Однако программы, использующие Win32 API, могут записывать строки UTF-16LE непосредственно в консоль WriteConsoleW. Это единственный способ получить правильный вывод без установки кодовых страниц. И даже при использовании этой функции, если строка не находится в кодировке UTF-16LE для начала, программа Win32 должна передать правильную кодовую страницу MultiByteToWideChar. Также WriteConsoleWне будет работать, если вывод программы перенаправлен; в этом случае нужно больше возиться.

typeработает некоторое время, потому что он проверяет начало каждого файла для метки порядка байтов UTF-16LE (BOM) , то есть байтов 0xFF 0xFE. Если он находит такую ​​метку, он отображает символы Unicode в файле, используя WriteConsoleW независимо от текущей кодовой страницы. Но при использовании typeлюбого файла без спецификации UTF-16LE или при использовании символов, отличных от ASCII, с любой командой, которая не вызывает WriteConsoleW, вам нужно будет настроить кодовую страницу консоли и кодировку вывода программы так, чтобы они соответствовали друг другу.


Как мы можем это выяснить?

Вот тестовый файл, содержащий символы Unicode:

ASCII     abcde xyz
German    äöü ÄÖÜ ß
Polish    ąęźżńł
Russian   абвгдеж эюя
CJK       你好

Вот Java-программа для распечатки тестового файла в куче разных кодировок Unicode. Это может быть на любом языке программирования; он печатает только символы ASCII или закодированные байты в stdout.

import java.io.*;

public class Foo {

    private static final String BOM = "\ufeff";
    private static final String TEST_STRING
        = "ASCII     abcde xyz\n"
        + "German    äöü ÄÖÜ ß\n"
        + "Polish    ąęźżńł\n"
        + "Russian   абвгдеж эюя\n"
        + "CJK       你好\n";

    public static void main(String[] args)
        throws Exception
    {
        String[] encodings = new String[] {
            "UTF-8", "UTF-16LE", "UTF-16BE", "UTF-32LE", "UTF-32BE" };

        for (String encoding: encodings) {
            System.out.println("== " + encoding);

            for (boolean writeBom: new Boolean[] {false, true}) {
                System.out.println(writeBom ? "= bom" : "= no bom");

                String output = (writeBom ? BOM : "") + TEST_STRING;
                byte[] bytes = output.getBytes(encoding);
                System.out.write(bytes);
                FileOutputStream out = new FileOutputStream("uc-test-"
                    + encoding + (writeBom ? "-bom.txt" : "-nobom.txt"));
                out.write(bytes);
                out.close();
            }
        }
    }
}

Выход в кодовой странице по умолчанию? Всего мусора!

Z:\andrew\projects\sx\1259084>chcp
Active code page: 850

Z:\andrew\projects\sx\1259084>java Foo
== UTF-8
= no bom
ASCII     abcde xyz
German    ├ñ├Â├╝ ├ä├û├£ ├ƒ
Polish    ąęźżńł
Russian   ð░ð▒ð▓ð│ð┤ðÁð ÐìÐÄÐÅ
CJK       õ¢áÕÑ¢
= bom
´╗┐ASCII     abcde xyz
German    ├ñ├Â├╝ ├ä├û├£ ├ƒ
Polish    ąęźżńł
Russian   ð░ð▒ð▓ð│ð┤ðÁð ÐìÐÄÐÅ
CJK       õ¢áÕÑ¢
== UTF-16LE
= no bom
A S C I I           a b c d e   x y z
 G e r m a n         õ ÷ ³   ─ Í ▄   ▀
 P o l i s h         ♣☺↓☺z☺|☺D☺B☺
 R u s s i a n       0♦1♦2♦3♦4♦5♦6♦  M♦N♦O♦
 C J K               `O}Y
 = bom
 ■A S C I I           a b c d e   x y z
 G e r m a n         õ ÷ ³   ─ Í ▄   ▀
 P o l i s h         ♣☺↓☺z☺|☺D☺B☺
 R u s s i a n       0♦1♦2♦3♦4♦5♦6♦  M♦N♦O♦
 C J K               `O}Y
 == UTF-16BE
= no bom
 A S C I I           a b c d e   x y z
 G e r m a n         õ ÷ ³   ─ Í ▄   ▀
 P o l i s h        ☺♣☺↓☺z☺|☺D☺B
 R u s s i a n      ♦0♦1♦2♦3♦4♦5♦6  ♦M♦N♦O
 C J K              O`Y}
= bom
■  A S C I I           a b c d e   x y z
 G e r m a n         õ ÷ ³   ─ Í ▄   ▀
 P o l i s h        ☺♣☺↓☺z☺|☺D☺B
 R u s s i a n      ♦0♦1♦2♦3♦4♦5♦6  ♦M♦N♦O
 C J K              O`Y}
== UTF-32LE
= no bom
A   S   C   I   I                       a   b   c   d   e       x   y   z
   G   e   r   m   a   n                   õ   ÷   ³       ─   Í   ▄       ▀
   P   o   l   i   s   h                   ♣☺  ↓☺  z☺  |☺  D☺  B☺
   R   u   s   s   i   a   n               0♦  1♦  2♦  3♦  4♦  5♦  6♦      M♦  N
♦  O♦
   C   J   K                               `O  }Y
   = bom
 ■  A   S   C   I   I                       a   b   c   d   e       x   y   z

   G   e   r   m   a   n                   õ   ÷   ³       ─   Í   ▄       ▀
   P   o   l   i   s   h                   ♣☺  ↓☺  z☺  |☺  D☺  B☺
   R   u   s   s   i   a   n               0♦  1♦  2♦  3♦  4♦  5♦  6♦      M♦  N
♦  O♦
   C   J   K                               `O  }Y
   == UTF-32BE
= no bom
   A   S   C   I   I                       a   b   c   d   e       x   y   z
   G   e   r   m   a   n                   õ   ÷   ³       ─   Í   ▄       ▀
   P   o   l   i   s   h                  ☺♣  ☺↓  ☺z  ☺|  ☺D  ☺B
   R   u   s   s   i   a   n              ♦0  ♦1  ♦2  ♦3  ♦4  ♦5  ♦6      ♦M  ♦N
  ♦O
   C   J   K                              O`  Y}
= bom
  ■    A   S   C   I   I                       a   b   c   d   e       x   y   z

   G   e   r   m   a   n                   õ   ÷   ³       ─   Í   ▄       ▀
   P   o   l   i   s   h                  ☺♣  ☺↓  ☺z  ☺|  ☺D  ☺B
   R   u   s   s   i   a   n              ♦0  ♦1  ♦2  ♦3  ♦4  ♦5  ♦6      ♦M  ♦N
  ♦O
   C   J   K                              O`  Y}

Тем не менее, что если мы typeсохраняем файлы? Они содержат те же байты, которые были напечатаны на консоли.

Z:\andrew\projects\sx\1259084>type *.txt

uc-test-UTF-16BE-bom.txt


■  A S C I I           a b c d e   x y z
 G e r m a n         õ ÷ ³   ─ Í ▄   ▀
 P o l i s h        ☺♣☺↓☺z☺|☺D☺B
 R u s s i a n      ♦0♦1♦2♦3♦4♦5♦6  ♦M♦N♦O
 C J K              O`Y}

uc-test-UTF-16BE-nobom.txt


 A S C I I           a b c d e   x y z
 G e r m a n         õ ÷ ³   ─ Í ▄   ▀
 P o l i s h        ☺♣☺↓☺z☺|☺D☺B
 R u s s i a n      ♦0♦1♦2♦3♦4♦5♦6  ♦M♦N♦O
 C J K              O`Y}

uc-test-UTF-16LE-bom.txt


ASCII     abcde xyz
German    äöü ÄÖÜ ß
Polish    ąęźżńł
Russian   абвгдеж эюя
CJK       你好

uc-test-UTF-16LE-nobom.txt


A S C I I           a b c d e   x y z
 G e r m a n         õ ÷ ³   ─ Í ▄   ▀
 P o l i s h         ♣☺↓☺z☺|☺D☺B☺
 R u s s i a n       0♦1♦2♦3♦4♦5♦6♦  M♦N♦O♦
 C J K               `O}Y

uc-test-UTF-32BE-bom.txt


  ■    A   S   C   I   I                       a   b   c   d   e       x   y   z

   G   e   r   m   a   n                   õ   ÷   ³       ─   Í   ▄       ▀
   P   o   l   i   s   h                  ☺♣  ☺↓  ☺z  ☺|  ☺D  ☺B
   R   u   s   s   i   a   n              ♦0  ♦1  ♦2  ♦3  ♦4  ♦5  ♦6      ♦M  ♦N
  ♦O
   C   J   K                              O`  Y}

uc-test-UTF-32BE-nobom.txt


   A   S   C   I   I                       a   b   c   d   e       x   y   z
   G   e   r   m   a   n                   õ   ÷   ³       ─   Í   ▄       ▀
   P   o   l   i   s   h                  ☺♣  ☺↓  ☺z  ☺|  ☺D  ☺B
   R   u   s   s   i   a   n              ♦0  ♦1  ♦2  ♦3  ♦4  ♦5  ♦6      ♦M  ♦N
  ♦O
   C   J   K                              O`  Y}

uc-test-UTF-32LE-bom.txt


 A S C I I           a b c d e   x y z
 G e r m a n         ä ö ü   Ä Ö Ü   ß
 P o l i s h         ą ę ź ż ń ł
 R u s s i a n       а б в г д е ж   э ю я
 C J K               你 好

uc-test-UTF-32LE-nobom.txt


A   S   C   I   I                       a   b   c   d   e       x   y   z
   G   e   r   m   a   n                   õ   ÷   ³       ─   Í   ▄       ▀
   P   o   l   i   s   h                   ♣☺  ↓☺  z☺  |☺  D☺  B☺
   R   u   s   s   i   a   n               0♦  1♦  2♦  3♦  4♦  5♦  6♦      M♦  N
♦  O♦
   C   J   K                               `O  }Y

uc-test-UTF-8-bom.txt


´╗┐ASCII     abcde xyz
German    ├ñ├Â├╝ ├ä├û├£ ├ƒ
Polish    ąęźżńł
Russian   ð░ð▒ð▓ð│ð┤ðÁð ÐìÐÄÐÅ
CJK       õ¢áÕÑ¢

uc-test-UTF-8-nobom.txt


ASCII     abcde xyz
German    ├ñ├Â├╝ ├ä├û├£ ├ƒ
Polish    ąęźżńł
Russian   ð░ð▒ð▓ð│ð┤ðÁð ÐìÐÄÐÅ
CJK       õ¢áÕÑ¢

Только вещь , которая работает в UTF-16LE файл с BOM, выводимого на консоль с помощьюtype .

Если мы используем что-то кроме typeпечати файла, мы получаем мусор:

Z:\andrew\projects\sx\1259084>copy uc-test-UTF-16LE-bom.txt CON
 ■A S C I I           a b c d e   x y z
 G e r m a n         õ ÷ ³   ─ Í ▄   ▀
 P o l i s h         ♣☺↓☺z☺|☺D☺B☺
 R u s s i a n       0♦1♦2♦3♦4♦5♦6♦  M♦N♦O♦
 C J K               `O}Y
         1 file(s) copied.

Из того факта, что copy CONUnicode отображает некорректно, можно сделать вывод, чтоtype команда имеет логику для определения спецификации UTF-16LE в начале файла и использования специальных API-интерфейсов Windows для ее печати.

Мы можем увидеть это, открыв cmd.exeв отладчике, когда он выходит type из файла:

введите описание изображения здесь

После того, typeкак файл открывается, он проверяет спецификацию - 0xFEFFт.е. байты с прямым порядком байтов 0xFF 0xFE- и, если такая спецификация существует, typeустанавливает внутренний fOutputUnicodeфлаг. Этот флаг проверяется позже, чтобы решить, стоит ли звонить WriteConsoleW.

Но это единственный способ получить typeUnicode и только для файлов, которые имеют спецификации и находятся в UTF-16LE. Для всех других файлов и для программ, которые не имеют специального кода для обработки вывода на консоль, ваши файлы будут интерпретироваться в соответствии с текущей кодовой страницей и, скорее всего, будут отображаться как бред.

Вы можете эмулировать, как typeвыводит Unicode на консоль в ваших собственных программах следующим образом:

#include <stdio.h>
#define UNICODE
#include <windows.h>

static LPCSTR lpcsTest =
    "ASCII     abcde xyz\n"
    "German    äöü ÄÖÜ ß\n"
    "Polish    ąęźżńł\n"
    "Russian   абвгдеж эюя\n"
    "CJK       你好\n";

int main() {
    int n;
    wchar_t buf[1024];

    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);

    n = MultiByteToWideChar(CP_UTF8, 0,
            lpcsTest, strlen(lpcsTest),
            buf, sizeof(buf));

    WriteConsole(hConsole, buf, n, &n, NULL);

    return 0;
}

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


Для примера Java-программы мы можем получить немного правильного вывода, установив кодовую страницу вручную, хотя выходные данные путаются странным образом:

Z:\andrew\projects\sx\1259084>chcp 65001
Active code page: 65001

Z:\andrew\projects\sx\1259084>java Foo
== UTF-8
= no bom
ASCII     abcde xyz
German    äöü ÄÖÜ ß
Polish    ąęźżńł
Russian   абвгдеж эюя
CJK       你好
ж эюя
CJK       你好
 你好
好
�
= bom
ASCII     abcde xyz
German    äöü ÄÖÜ ß
Polish    ąęźżńł
Russian   абвгдеж эюя
CJK       你好
еж эюя
CJK       你好
  你好
好
�
== UTF-16LE
= no bom
A S C I I           a b c d e   x y z
…

Однако, программа на C, которая устанавливает кодовую страницу Unicode UTF-8:

#include <stdio.h>
#include <windows.h>

int main() {
    int c, n;
    UINT oldCodePage;
    char buf[1024];

    oldCodePage = GetConsoleOutputCP();
    if (!SetConsoleOutputCP(65001)) {
        printf("error\n");
    }

    freopen("uc-test-UTF-8-nobom.txt", "rb", stdin);
    n = fread(buf, sizeof(buf[0]), sizeof(buf), stdin);
    fwrite(buf, sizeof(buf[0]), n, stdout);

    SetConsoleOutputCP(oldCodePage);

    return 0;
}

имеет правильный вывод:

Z:\andrew\projects\sx\1259084>.\test
ASCII     abcde xyz
German    äöü ÄÖÜ ß
Polish    ąęźżńł
Russian   абвгдеж эюя
CJK       你好

Мораль этой истории?

  • type может печатать файлы UTF-16LE с спецификацией независимо от вашей текущей кодовой страницы
  • Программы Win32 могут быть запрограммированы для вывода Unicode на консоль, используя WriteConsoleW.
  • Другие программы, которые устанавливают кодовую страницу и соответственно корректируют свою выходную кодировку, могут печатать Unicode на консоли независимо от того, какой была кодовая страница при запуске программы.
  • Со всем остальным вам придется возиться chcp, и, вероятно, вы получите странные результаты.
andrewdotn
источник
73
Ого, это, наверное, самый подробный ответ, который я когда-либо видел на SO. Дополнительный кредит для распечатки и мультиязычности skillz! Просто красиво, сэр!
авиаудар
2
Можно также захотеть изучить специфическое для Microsoft расширение _setmode (_fileno (stdout), _O_U16TEXT), которое было представлено в VS2008. См stackoverflow.com/a/9051543 и stackoverflow.com/a/12015918 и msdn.microsoft.com/en-us/library/tw4k6df8(v=vs.90).aspx Помимо очевидных различий между переносимостью _setmode () и SetConsoleOutputCP (), могут также быть другие тонкости и побочные эффекты, скрытые в обоих подходах, которые не полностью поняты на первый взгляд. Если бы andrewdotn мог обновить свой ответ любыми замечаниями по поводу _setmode (fd, _O_U16TEXT), это было бы здорово.
JasDev
13
Хотя это отличный ответ, вводить в заблуждение утверждение, что консоль поддерживает UTF-16. Это ограничено UCS-2, то есть ограничено символами в основной многоязычной плоскости (BMP). Когда консольный сервер Win32 (в настоящее время conhost.exe) был спроектирован примерно в 1990 году, Unicode был 16-битным стандартом, поэтому экранный буфер консоли использует один 16-битный WCHAR на символьную ячейку. Суррогатная пара UTF-16 печатается в виде двух символов коробки.
Eryk Sun
3
@ user200783, разложенная форма не поддерживается; обычно можно преобразовать в эквивалент NFC. Кроме того, консоль в западных локалях не позволяет смешивать глифы полной ширины и полуширины. Кроме того, при использовании кодовой страницы 65001 (UTF-8) до Windows 8 WriteFileсообщается о количестве записанных символов вместо количества байтов, поэтому буферизованные записывающие устройства повторяют «оставшиеся» байты несколько раз пропорционально количеству не-ASCII символов. , Также в 65001 чтение не-ASCII-символов завершается неудачно в conhost.exe, поскольку при вызове он принимает 1 ANSI-байт на код UTF-16 WideCharToMultiByte.
Eryk Sun
2
Простые демонстрационные программы в этом ответе предполагают, что GetStdHandle(STD_OUTPUT_HANDLE)и C stdoutявляются дескрипторами консоли. На практике, чтобы проверить консоль, проверьте, что это GetConsoleModeуспешно. Кроме того, не используйте _isattyфункцию времени выполнения C, чтобы проверить, является ли дескриптор файла с низким I / O консолью; это просто проверяет устройство в символьном режиме, которое включает в себя, NULсреди прочего. Вместо этого позвоните _get_osfhandleи проверьте ручку напрямую.
Eryk Sun
29

Тип

chcp

чтобы увидеть вашу текущую кодовую страницу (как уже сказал Dewfy).

использование

nlsinfo

чтобы увидеть все установленные кодовые страницы и узнать, что означает номер вашей кодовой страницы.

Для использования необходимо установить комплект ресурсов Windows Server 2003 (работает на Windows XP) nlsinfo.

Cagdas Altinkaya
источник
19
Интересно, nlsinfoчто , кажется, не существует на моей Windows 7.
Джои
2
nlsinfoтакже не существует на моем компьютере с Windows XP SP3.
Томас Оуэнс
2
Ой, простите. Я думаю, что это идет с инструментами Windows Server Resource Kit. Я использовал его пару раз на своем компьютере с Windows XP SP3 ранее и не знал, что он не был установлен по умолчанию.
Cagdas Altinkaya
Ах, это объясняет, почему это там на моей машине Vista, где я их установил.
Джои
4
nlsinfoтакже не существует на компьютере с Windows 10E.
Юша Алеауб
21

Чтобы ответить на ваш второй запрос повторно. Как работает кодирование, Джоэл Спольски написал отличную вводную статью по этому вопросу . Настоятельно рекомендуется.

Брайан Агнью
источник
13
Я прочитал это, и я знаю это. Тем не менее, в Windows я всегда чувствую себя потерянным, потому что ОС и большинство приложений кажутся совершенно неосведомленными о кодировании.
danglund
5

Команда CHCP показывает текущую кодовую страницу. Он состоит из трех цифр: 8xx и отличается от Windows 12xx. Поэтому, печатая текст на английском языке, вы не увидите никакой разницы, но расширенная кодовая страница (например, кириллица) будет напечатана неправильно.

Dewfy
источник
5
CHCP не показывает только 3 цифры и не в формате 8 ##. 437, например, является кодировкой США и стандартом де-факто в английских системах. - 65001 - это кодировка Unicode (если я правильно помню, это UTF-8, а 65000 - UTF-7), и ее можно выбрать. Также CMD позволяет переключаться на кодовую страницу 1250, например, но я не знаю с тех пор, когда эти кодовые страницы можно выбрать. (Это под Win7.)
Адам Л.С.
4

Я долго расстраивался из-за проблем с кодовыми страницами Windows, а также из-за проблем переносимости и локализации программ на Си. В предыдущих постах подробно рассказывалось о проблемах, поэтому я не собираюсь ничего добавлять на этот счет.

Короче говоря, в конце концов я закончил писать свой собственный уровень библиотеки совместимости UTF-8 поверх стандартной библиотеки C Visual C ++. По сути, эта библиотека гарантирует, что стандартная программа на C работает правильно, в любой кодовой странице, используя UTF-8 внутри.

Эта библиотека, которая называется MsvcLibX, доступна в открытом доступе по адресу https://github.com/JFLarvoire/SysToolsLib . Основные характеристики:

  • Исходники C, закодированные в UTF-8, с использованием обычных строк char [] C и стандартных API-интерфейсов библиотеки C.
  • В любой кодовой странице все внутри вашего кода обрабатывается как UTF-8, включая подпрограмму main () argv [], при этом стандартный ввод и вывод автоматически преобразуются в правильную кодовую страницу.
  • Все функции файла stdio.h поддерживают имена путей UTF-8> 260 символов, на самом деле до 64 КБ.
  • Те же источники могут успешно компилироваться и компоноваться в Windows с использованием библиотек Visual C ++ и MsvcLibX и Visual C ++, а также в Linux с использованием стандартной библиотеки C gcc и Linux, без необходимости в блоках #ifdef ... #endif.
  • Добавляет включаемые файлы, распространенные в Linux, но отсутствующие в Visual C ++. Пример: unistd.h
  • Добавляет недостающие функции, такие как функции ввода-вывода в каталогах, управления символическими ссылками и т. Д., Конечно, с поддержкой UTF-8 :-).

Более подробная информация в MsvcLibX README на GitHub , в том числе о том, как собрать библиотеку и использовать ее в своих собственных программах.

Раздел выпуска в вышеупомянутом репозитории GitHub предоставляет несколько программ, использующих эту библиотеку MsvcLibX, которые покажут ее возможности. Пример: попробуйте мой инструмент which.exe с каталогами с не-ASCII-именами в PATH, поиск программ с не-ASCII-именами и изменение кодовых страниц.

Еще одним полезным инструментом является программа conv.exe. Эта программа может легко конвертировать поток данных из любой кодовой страницы в любую другую. По умолчанию он вводится в кодовой странице Windows и выводится в текущей кодовой странице консоли. Это позволяет правильно просматривать данные, сгенерированные приложениями Windows GUI (например, Notepad) в командной консоли, с помощью простой команды, такой как:type WINFILE.txt | conv

Эта библиотека MsvcLibX ни в коем случае не является полной, и мы приветствуем вклад в ее улучшение!

Жан-Франсуа Ларвуар
источник
2

В Java я использовал кодировку «IBM850» для записи файла. Это решило проблему.

Neumi
источник