Яку кодування / кодову сторінку використовує cmd.exe?


271

Коли я відкриваю cmd.exe в Windows, яке кодування воно використовує?

Як я можу перевірити, яке кодування в даний час використовується? Це залежить від моїх регіональних налаштувань чи є якісь змінні середовища для перевірки?

Що відбувається, коли ви вводите файл із певним кодуванням? Іноді я отримую зіткнуті символи (використовується неправильне кодування), а іноді це працює. Однак я нікому не вірю, доки не знаю, що відбувається. Хтось може пояснити?

Відповіді:


389

Так, це засмучує: іноді typeй інші програми друкують хитрість, а іноді й ні.

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

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

Коли програми використовують стандартні функції вводу / виводу бібліотеки С, як-от printf, кодування виводу програми повинно відповідати вихідному кодуванню консолі , інакше ви отримаєте хитрість. chcpпоказує та встановлює поточну кодову сторінку. Весь вихід із використанням стандартних функцій вводу / виводу бібліотеки С-трактується так, ніби він знаходиться в кодовій сторінці, яку відображаєchcp .

Зіставлення кодування виводу програми з кодуванням виходу консолі може здійснюватися двома різними способами:

  • Програма може отримати поточну кодову сторінку консолі, використовуючи chcpабо GetConsoleOutputCP, і налаштувати себе на вихід у кодуванні, або

  • Ви або програма можете встановити поточну кодову сторінку консолі, використовуючи chcpабо SetConsoleOutputCPвідповідати кодуванню виводу за замовчуванням програми.

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

typeпрацює деякий час, тому що він перевіряє початок кожного файлу для позначення порядку байтів UTF-16LE (BOM) , тобто байтів 0xFF 0xFE. Якщо він знаходить таку позначку, він відображає символи Unicode у файлі, використовуючи WriteConsoleW незалежно від поточної сторінки коду. Але коли ви typeкористуєтесь будь-яким файлом без UTF-16LE BOM або для використання символів, що не належать до 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команда має логіку виявлення BT UTF-16LE на початку файлу та використання спеціальних API для друку Windows.

Це ми можемо побачити, відкривши cmd.exeв налагоджувачі, коли він виходить type з файлу:

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

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

Але це єдиний спосіб дістатися type до виводу Unicode і лише для файлів, які мають BOM і знаходяться в 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 з BOM незалежно від поточної кодової сторінки
  • Програми Win32 можна запрограмувати для виведення Unicode на консоль, використовуючи WriteConsoleW.
  • Інші програми, які встановлюють кодову сторінку та відповідно коригують їх кодування, можуть надрукувати Unicode на консолі незалежно від того, якою була сторінка коду при запуску програми
  • З усім іншим вам доведеться возитися chcp, і, ймовірно, все одно вийде дивний вихід.

73
Ого, це, мабуть, найдокладніша відповідь, яку я коли-небудь бачив на ПП. Додатковий кредит за роздруківку відбитків та багатомовну майстерність! Просто красиво, сер!
авіаудар

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 друкується у вигляді двох символів.
Ерик Нд

3
@ user200783, розкладена форма не підтримується; зазвичай можна перетворитись на еквівалент NFC. Крім того, консоль у західних локалях не дозволяє змішувати гліфи повної ширини та напівширини. Крім того, коли використовується кодова сторінка 65001 (UTF-8), перед Windows 8 WriteFileповідомляється про кількість символів, записаних замість кількості байтів, тож забудовані письменники повторюють повторний обсяг байтів кілька разів пропорційно кількості символів, що не належать до ASCII . Крім того, в 65001, читання символів, що не належать до ASCII, виходить з ладу в conhost.exe, оскільки він передбачає 1 байт ANSI на код UTF-16 під час виклику WideCharToMultiByte.
Ерік Нд

2
Прості демонстраційні програми в цій відповіді припускають, що GetStdHandle(STD_OUTPUT_HANDLE)і C stdout- консольні ручки. На практиці для тестування консолі перевірте, чи GetConsoleModeвдалося. Крім того, не використовуйте функцію виконання C, _isattyщоб перевірити, чи низький дескриптор файлу вводу / виводу - консоль; що просто перевіряє наявність пристрою в режимі символів, який включає NULсеред інших. Натомість зателефонуйте _get_osfhandleта перевірте ручку безпосередньо.
Ерик Вс

29

Тип

chcp

щоб побачити вашу поточну кодову сторінку (як уже сказав Dewfy).

Використовуйте

nlsinfo

щоб побачити всі встановлені кодові сторінки та дізнатися, що означає номер вашої кодової сторінки.

Для використання потрібно встановити комплект ресурсів Windows Server 2003 (працює на Windows XP) nlsinfo.


19
Цікаво, nlsinfoщо на моєму Windows 7 не існує.
Joey

2
nlsinfoтакож не існує на моїй машині Windows XP SP3.
Томас Оуенс

2
О, пробач. Я думаю, що це поставляється з інструментами набору ресурсів Windows Server. Я використовував його кілька разів на своїй машині Windows XP SP3 раніше, і не знав, що він не встановлений за замовчуванням.
Cagdas Altinkaya

Так, це пояснює, чому саме там на моїй машині Vista, куди я їх встановив.
Джої

4
nlsinfoтакож не існує на машині Windows 10E.
Yousha Aleayoub

21

Щоб відповісти на ваш другий запит, повторно. як працює кодування, Джоел Спольський написав про це чудову вступну статтю . Настійно рекомендується.


13
Я прочитав і це знаю. Однак у Windows я завжди відчуваю себе втраченою, оскільки ОС і більшість програм здаються абсолютно невідомими щодо кодування.
danglund

5

Команда CHCP показує поточну кодову сторінку. Він має три цифри: 8xx та відрізняється від Windows 12xx. Таким чином, набравши текст лише англійською мовою, ви не побачили б різниці, але розширена кодова сторінка (наприклад, кирилиця) буде надрукована неправильно.


5
CHCP не показує лише 3 цифри, а також не у форматі 8 ##. 437, наприклад, кодування в США, і це стандарт дефакто в англійських системах. - 65001 - це кодування Unicode (якщо я пам'ятаю це правильно, це UTF-8, а 65000 - UTF-7), і його можна вибрати. Також CMD дозволяє перейти, наприклад, до кодової сторінки 1250, але я не знаю, з тих пір, коли ці кодові сторінки можна вибрати. (Це під Win7.)
Адам LS

4

Мене довго неприємно викликати проблеми кодової сторінки Windows, а також проблеми, пов'язані з переносом та локалізацією програм C. Попередні публікації детально розглядали питання, тому я нічого не збираюся додати у цьому відношенні.

Щоб коротко розповісти, я врешті-решт написав власний рівень бібліотеки сумісності UTF-8 через стандартну бібліотеку C ++ Visual C ++. В основному ця бібліотека забезпечує нормальну роботу програми C на будь-якій кодовій сторінці, використовуючи внутрішньо UTF-8.

Ця бібліотека під назвою MsvcLibX доступна як відкритий код на веб- сайті https://github.com/JFLarvoire/SysToolsLib . Основні риси:

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

Детальніше в MsvcLibX README на GitHub , зокрема про те, як створити бібліотеку та використовувати її у власних програмах.

Розділ випуску у вищезгаданому сховищі GitHub надає декілька програм, що використовують цю бібліотеку MsvcLibX, які покажуть її можливості. Наприклад: спробуйте мій інструмент that.exe з каталогами з іменами, що не належать до ASCII, в PATH, пошук програм з іменами, що не належать до ASCII, та змінення кодових сторінок.

Іншим корисним інструментом є програма conv.exe. Ця програма може легко перетворити потік даних з будь-якої кодової сторінки на будь-яку іншу. Його за замовчуванням вводиться на кодовій сторінці Windows, а виводиться на поточній кодовій сторінці консолі. Це дозволяє правильно переглядати дані, створені програмами GUI Windows (наприклад: Блокнот) на командній консолі, за допомогою простої команди, наприклад:type WINFILE.txt | conv

Ця бібліотека MsvcLibX аж ніяк не повна, і внески для її вдосконалення вітаються!


2

У Java я використовував кодування "IBM850" для написання файлу. Це вирішило проблему.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.