回答:
はい、それはイライラします—時々type
および他のプログラムは意味不明なものを印刷し、時には彼らはしません。
まず、Unicode文字は、現在のコンソールフォントに文字が含まれている場合にのみ表示されます。したがって、デフォルトのラスターフォントではなく、Lucida ConsoleのようなTrueTypeフォントを使用してください。
ただし、コンソールフォントに表示しようとしている文字が含まれていない場合は、意味不明な文字ではなく疑問符が表示されます。意味不明になったら、フォントの設定だけではありません。
プログラムは以下のように、標準CライブラリI / O関数を使用するとprintf
、プログラムの出力エンコーディングは、コンソールの出力エンコーディングと一致する必要があります、またはあなたはちんぷんかんぷんを取得します。chcp
現在のコードページを表示および設定します。標準のCライブラリI / O関数を使用するすべての出力は、によって表示されるコードページにあるかのように扱われますchcp
。
プログラムの出力エンコーディングとコンソールの出力エンコーディングを一致させるには、次の2つの方法があります。
プログラムは、chcp
または
を使用してコンソールの現在のコードページを取得し、GetConsoleOutputCP
そのエンコーディングで出力するようにプログラム自体を構成できます。
ユーザーまたはプログラムは、chcp
または
SetConsoleOutputCP
を使用してコンソールの現在のコードページを設定し、プログラムのデフォルトの出力エンコーディングと一致させることができます。
ただし、Win32 APIを使用するプログラムは、を使用してUTF-16LE文字列をコンソールに直接書き込むことができます
WriteConsoleW
。これは、コードページを設定せずに正しい出力を得る唯一の方法です。また、その関数を使用する場合でも、文字列が最初からUTF-16LEエンコーディングになっていない場合、Win32プログラムは正しいコードページをに渡す必要があります
MultiByteToWideChar
。また、WriteConsoleW
プログラムの出力がリダイレクトされている場合は機能しません。その場合はもっといじる必要があります。
type
は、各ファイルの先頭でUTF-16LE バイトオーダーマーク(BOM)、つまりバイトをチェックするため、ときどき動作します0xFF 0xFE
。そのようなマークが見つかるWriteConsoleW
と、現在のコードページに関係なく、ファイル内のUnicode文字が表示されます。ただしtype
、UTF-16LE BOMのないファイルを呼び出す場合、または呼び出しを行わないコマンドで非ASCII文字を使用WriteConsoleW
する場合は、コンソールのコードページとプログラムの出力エンコーディングを互いに一致するように設定する必要があります。
どうすればこれを見つけることができますか?
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を使用してそれを印刷することができます。
これを確認するに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;
}
このプログラムは、デフォルトのコードページを使用して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
。chcp
、おそらく変な出力が得られます。WriteFile
では、バイト数ではなく書き込まれた文字数が報告されるため、バッファーライターは「残りの」バイトを非ASCII文字数に比例して数回再試行します。 。また、65001では、を呼び出すときにUTF-16コードごとに1 ANSIバイトを想定するため、conhost.exeで非ASCII文字の読み取りが失敗しWideCharToMultiByte
ます。
GetStdHandle(STD_OUTPUT_HANDLE)
、C stdout
がコンソールハンドルであることを前提としています。実際には、コンソールをテストするには、それGetConsoleMode
が成功することを確認します。また、Cランタイム_isatty
関数を使用して、低I / Oファイル記述子がコンソールかどうかを確認しないでください。これは、文字モードデバイス(NUL
他のものを含む)をチェックするだけです。代わりに、_get_osfhandle
直接呼び出してハンドルを確認してください。
タイプ
chcp
(Dewfyが既に言ったように)現在のコードページを表示します。
使用する
nlsinfo
インストールされているすべてのコードページを確認し、コードページ番号の意味を確認します。
を使用するには、Windows Server 2003リソースキットをインストールする必要があります(Windows XPで動作)nlsinfo
。
nlsinfo
私のWindows 7には存在しないようです
nlsinfo
私のWindows XP SP3マシンにも存在しません。
nlsinfo
Windows 10Eマシンにも存在しません。
コマンドCHCPは現在のコードページを表示します。3桁:8xxで、Windows 12xxとは異なります。したがって、英語のみのテキストを入力しても違いはありませんが、拡張コードページ(キリル文字など)が誤って印刷されます。
Windowsコードページの問題と、Cプログラムの移植性とローカリゼーションの問題が原因で、私は長い間イライラしてきました。以前の投稿では問題を詳細に説明しているため、この点については何も追加しません。
長い話を簡単にするために、結局私はVisual C ++標準Cライブラリーの上に自分のUTF-8互換性ライブラリー層を作成することになりました。基本的にこのライブラリは、UTF-8を内部で使用して、標準のCプログラムがどのコードページでも正しく機能することを保証します。
MsvcLibXと呼ばれるこのライブラリは、https://github.com/JFLarvoire/SysToolsLibでオープンソースとして入手できます。主な特徴:
GitHubのMsvcLibX READMEに詳細が記載されています。これには、ライブラリを構築して独自のプログラムで使用する方法が含まれます。
上記のGitHubリポジトリのリリースセクションには、このMsvcLibXライブラリを使用するいくつかのプログラムがあり、その機能を示します。例:PATHに非ASCII名のディレクトリがあるmy.exeツールを試し、非ASCII名のプログラムを検索し、コードページを変更します。
そこに別の便利なツールはconv.exeプログラムです。このプログラムは、データストリームを任意のコードページから任意のコードページに簡単に変換できます。デフォルトは、Windowsコードページでの入力で、現在のコンソールコードページでの出力です。これにより、次のような簡単なコマンドを使用して、Windows GUIアプリ(メモ帳など)によって生成されたデータをコマンドコンソールで正しく表示できます。type WINFILE.txt | conv
このMsvcLibXライブラリは完全ではありません。ライブラリを改善するための貢献を歓迎します!