Windowsでcmd.exeを開くと、どのエンコーディングを使用していますか?
現在どのエンコードを使用しているかを確認するにはどうすればよいですか。それは私の地域の設定に依存しますか、またはチェックするべき環境変数がありますか?
特定のエンコーディングのファイルを入力するとどうなりますか?文字が文字化けしたり(誤ったエンコーディングが使用されたり)、時にはそれがうまくいくことがあります。しかし、何が起こっているのかわからない限り、私は何も信頼していません。誰か説明できますか?
はい、苛立たしいことがあります - 時にはtype
や他のプログラムはちんぷんかんぷんと印刷しますが、時にはそうではありません。
まず第一に、Unicode文字は 現在のコンソールフォントに文字が含まれている場合 だけを表示します。そのため、デフォルトのRasterフォントの代わりにLucida ConsoleのようなTrueTypeフォントを使用してください。
ただし、コンソールのフォントに表示しようとしている文字が含まれていない場合は、意味不明の代わりに疑問符が表示されます。ちょっとおかしなことになると、フォントの設定以外にもさまざまなことが起こります。
プログラムがprintf
、のような標準のCライブラリI/O関数を使用する場合、プログラムの出力エンコーディングはコンソールの出力エンコーディングと一致する必要があります。 chcp
は、現在のコードページを表示および設定します。標準CライブラリI/O関数を使用したすべての出力は、chcp
で表示されるコードページにあるかのように扱われます。
プログラムの出力エンコーディングとコンソールの出力エンコーディングを一致させるには、2つの方法があります。
プログラムはchcp
または GetConsoleOutputCP
を使用してコンソールの現在のコードページを取得し、そのエンコーディングで出力するように設定することができます。
あなたやプログラムは、プログラムのデフォルトの出力エンコーディングと一致させるためにchcp
または SetConsoleOutputCP
を使ってコンソールの現在のコードページを設定することができます。
ただし、Win32 APIを使用するプログラムは、 WriteConsoleW
を使用してUTF-16LE文字列を直接コンソールに書き込むことができます。これは、コードページを設定せずに正しい出力を取得する唯一の方法です。また、その関数を使用しているときでも、文字列がUTF-16LEエンコーディングで始まっていない場合、Win32プログラムは正しいコードページを MultiByteToWideChar
に渡す必要があります。また、プログラムの出力がリダイレクトされている場合、WriteConsoleW
は機能しません。その場合はもっといじる必要があります。
type
は各ファイルの先頭でUTF-16LE Byte Order Mark(BOM) 、つまりバイト0xFF 0xFE
をチェックするので、しばらくの間は機能します。そのようなマークが見つかった場合、現在のコードページに関係なく、WriteConsoleW
を使用してファイル内のUnicode文字を表示します。しかし、UTF-16LE BOMなしのファイルをtype
する場合、またはWriteConsoleW
を呼び出さないコマンドでASCII以外の文字を使用する場合は、コンソールのコードページとプログラムの出力エンコードを互いに一致するように設定する必要があります。
これをどうやって見つけることができるでしょうか。
これがUnicode文字を含むテストファイルです。
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
これは、テストファイルをさまざまなUnicodeエンコードで印刷するためのJavaプログラムです。どんなプログラミング言語でも可能です。 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 õ¢áÕÑ¢
のみ動作するのは、BOM付きのUTF-16LEファイルで、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 CON
がUnicodeを正しく表示しないという事実から、type
コマンドにはファイルの先頭でUTF-16LE BOMを検出するロジックがあり、それを印刷するために特別なWindows APIを使用すると結論付けることができます。
これは、ファイルからtype
に移動するときにデバッガでcmd.exe
を開くことで確認できます。
type
はファイルを開いた後、0xFEFF
のBOM(つまり、リトルエンディアンの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;
}
このプログラムは、既定のコードページを使用してWindowsコンソールにUnicodeを印刷するために機能します。
サンプル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
…
ただし、Unicode UTF-8コードページを設定するCプログラムは、次のとおりです。
#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
は、現在のコードページに関係なく、BOM付きのUTF-16LEファイルを印刷できます。WriteConsoleW
を使用して、コンソールにUnicodeを出力するようにプログラムできます。chcp
をいじる必要がありますが、おそらくまだ変な出力になるでしょう。タイプ
chcp
あなたの現在のコードページを見るために(Dewfyが既に言ったように)。
つかいます
nlsinfo
インストールされているすべてのコードページを確認し、自分のコードページ番号の意味を確認する。
nlsinfo
を使用するには、Windows Server 2003リソースキットをインストールする必要があります(Windows XP上で動作します)。
2番目の質問に答えるには、エンコーディングのしくみについて、Joel Spolskyはすばらしい これに関する入門記事 を書きました。強く推奨する。
コマンドCHCPは現在のコードページを表示します。それは3桁の数字を持っています:8xxとWindows 12xxとは異なります。そのため、英語のみのテキストを入力しても違いはありませんが、(キリル文字のような)拡張コードページは誤って印刷されます。
私は長い間、Windowsのコードページの問題と、それらが引き起こすCプログラムの移植性とローカライゼーションの問題に不満を感じていました。これまでの投稿で問題の詳細を詳しく説明してきたので、この点に関しては何も追加しません。
長い話を簡単にすると、結局私はVisual C++標準Cライブラリの上に自分のUTF-8互換ライブラリ層を書いてしまいました。基本的にこのライブラリは、標準CプログラムがどのコードページでもUTF-8を内部的に使用して正しく機能することを保証します。
MsvcLibXと呼ばれるこのライブラリは、 https://github.com/JFLarvoire/SysToolsLib からオープンソースとして入手できます。主な特徴:
GitHubのMsvcLibX README に、ライブラリの作成方法や自分のプログラムでの使用方法などの詳細が記載されています。
上記のGitHubリポジトリの release section は、このMsvcLibXライブラリを使ったいくつかのプログラムを提供しています。例:PATHにASCII以外の名前のディレクトリを指定してwhich.exeツールを試し、ASCII以外の名前のプログラムを検索し、コードページを変更してください。
別の便利なツールがconv.exeプログラムです。このプログラムは、データストリームを任意のコードページから他のコードページに簡単に変換できます。デフォルトはWindowsコードページで入力され、現在のコンソールコードページで出力されます。これにより、WindowsのGUIアプリケーション(例:メモ帳)で生成されたデータをコマンドコンソールで正しく表示することができます。これは次のような単純なコマンドです。type WINFILE.txt | conv
このMsvcLibXライブラリは決して完全なものではなく、それを改善するための貢献は大歓迎です!
Javaでは、ファイルの書き込みに「IBM850」のエンコードを使用しました。これで問題は解決しました。