답변:
그렇습니다. 때로는 실망 스럽습니다. 때로는 type
다른 프로그램이 횡설수설을하지만 때로는 그렇지 않습니다.
우선, 유니 코드 문자는 현재 콘솔 글꼴에 문자가 포함 된 경우 에만 표시 됩니다 . 따라서 기본 래스터 글꼴 대신 Lucida Console과 같은 트루 타입 글꼴을 사용하십시오.
그러나 콘솔 글꼴에 표시하려는 문자가 포함되어 있지 않으면 횡설수설 문자 대신 물음표가 표시됩니다. 횡설수설하면 글꼴 설정보다 더 많은 일이 진행됩니다.
프로그램이 같은 표준 C 라이브러리 I / O 기능을 사용하는 경우 printf
, 프로그램의 출력 인코딩은 콘솔의 출력 인코딩과 일치해야 하거나 횡설수설을 얻을 것이다. chcp
현재 코드 페이지를 표시하고 설정합니다. 표준 C 라이브러리 I / O 함수를 사용하는 모든 출력은로 표시된 코드 페이지에있는 것처럼 처리됩니다 chcp
.
프로그램의 출력 인코딩을 콘솔의 출력 인코딩과 일치시키는 방법은 두 가지 방법으로 수행 할 수 있습니다.
프로그램은 사용하여 콘솔의 현재 코드 페이지를 얻을 수있다 chcp
거나
GetConsoleOutputCP
, 그 인코딩으로 출력 자체를 구성하거나
사용자 또는 프로그램은 사용하여 콘솔의 현재 코드 페이지를 설정할 수 있습니다 chcp
또는
SetConsoleOutputCP
프로그램의 기본 출력 인코딩과 일치 할 수 있습니다.
그러나 Win32 API를 사용하는 프로그램은을 사용하여 UTF-16LE 문자열을 콘솔에 직접 쓸 수 있습니다
WriteConsoleW
. 이것은 코드 페이지를 설정하지 않고 올바른 출력을 얻는 유일한 방법입니다. 그리고이 함수를 사용할 때조차도 문자열이 UTF-16LE 인코딩으로 시작하지 않으면 Win32 프로그램이 올바른 코드 페이지를에 전달해야합니다
MultiByteToWideChar
. 또한 WriteConsoleW
프로그램의 출력이 리디렉션되면 작동하지 않습니다. 이 경우 더 많은 조정이 필요합니다.
type
UTF-16LE BOM (Byte Order Mark) , 즉 bytes 에 대해 각 파일의 시작을 검사하기 때문에 어느 정도 시간이 걸립니다 0xFF 0xFE
. 그러한 마크를 찾으면 WriteConsoleW
현재 코드 페이지에 관계없이 파일에 유니 코드 문자를 표시합니다 . 그러나 type
UTF-16LE BOM이없는 파일을 호출하거나 호출하지 않는 명령에 비 ASCII 문자를 사용하는 WriteConsoleW
경우 콘솔 코드 페이지와 프로그램 출력 인코딩을 서로 일치하도록 설정해야합니다.
우리는 이것을 어떻게 알 수 있습니까?
다음은 유니 코드 문자가 포함 된 테스트 파일입니다.
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
다음은 테스트 파일을 다양한 유니 코드 인코딩으로 인쇄하는 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 õ¢áÕÑ¢
작동 하는 유일한 것은 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 CON
유니 코드를 올바르게 표시하지 않는다는 사실 에서 type
명령 시작시 파일 시작시 UTF-16LE BOM을 감지하고 특수한 Windows API를 사용하여 인쇄하는 논리가 있다고 결론 내릴 수 있습니다 .
우리는
파일 cmd.exe
을 type
나갈 때 디버거 를 열어서 이것을 볼 수 있습니다 .
type
파일을 연 후에 는 BOM ( 0xFEFF
즉 0xFF 0xFE
리틀 엔디안 의 바이트)
을 확인하고 이러한 BOM이 type
있으면 내부 fOutputUnicode
플래그를 설정합니다 . 이 플래그는 나중에 호출 여부를 결정하기 위해 점검됩니다 WriteConsoleW
.
그러나 이것이 type
BOM이 있고 UTF-16LE 인 파일에 대해서만 유니 코드를 출력 하는 유일한 방법 입니다. 다른 모든 파일 및 콘솔 출력을 처리하기위한 특수 코드가없는 프로그램의 경우 파일은 현재 코드 페이지에 따라 해석되며 횡설수설로 표시 될 수 있습니다.
다음 type
과 같이 자신의 프로그램에서 유니 코드를 콘솔에 출력 하는 방법을 에뮬레이션 할 수 있습니다 .
#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 콘솔에서 유니 코드를 인쇄하는 데 사용됩니다.
샘플 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
…
그러나 유니 코드 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에서 실패합니다 WideCharToMultiByte
.
GetStdHandle(STD_OUTPUT_HANDLE)
C stdout
가 콘솔 핸들 이라고 가정 합니다. 실제로 콘솔을 테스트하려면 GetConsoleMode
성공했는지 확인하십시오 . 또한 _isatty
낮은 I / O 파일 디스크립터가 콘솔인지 확인 하기 위해 C 런타임 함수를 사용하지 마십시오 . 그것은 문자 모드 장치를 검사 NUL
합니다. 대신 _get_osfhandle
핸들을 직접 호출 하여 확인하십시오.
유형
chcp
Dewfy가 이미 말한 것처럼 현재 코드 페이지를 볼 수 있습니다.
사용하다
nlsinfo
설치된 모든 코드 페이지를보고 코드 페이지 번호의 의미를 확인하십시오.
를 사용하려면 Windows Server 2003 Resource Kit가 설치되어 있어야합니다 (Windows XP에서 작동) nlsinfo
.
nlsinfo
내 Windows 7에는 존재하지 않는 것 같습니다.
nlsinfo
Windows XP SP3 컴퓨터에도 없습니다.
nlsinfo
Windows 10E 컴퓨터에는 존재하지 않습니다.
CHCP 명령은 현재 코드 페이지를 보여줍니다. 8xx의 세 자리 숫자가 있으며 Windows 12xx와 다릅니다. 따라서 영어 전용 텍스트를 입력해도 아무런 차이가 없지만 키릴 자모와 같은 확장 코드 페이지가 잘못 인쇄됩니다.
Windows 코드 페이지 문제와 C 프로그램 이식성 및 현지화 문제로 오랫동안 좌절했습니다. 이전 게시물에는 문제가 자세히 설명되어 있으므로이 점에서 아무것도 추가하지 않겠습니다.
간단히 말해서 결국 Visual C ++ 표준 C 라이브러리를 통해 자체 UTF-8 호환성 라이브러리 계층을 작성했습니다. 기본적으로이 라이브러리는 표준 C 프로그램이 UTF-8을 내부적으로 사용하여 모든 코드 페이지에서 올바르게 작동하도록합니다.
MsvcLibX라고하는이 라이브러리는 https://github.com/JFLarvoire/SysToolsLib 에서 오픈 소스로 제공됩니다 . 주요 특징:
라이브러리를 빌드하고 자신의 프로그램에서 사용하는 방법을 포함하여 GitHub 의 MsvcLibX README에 대한 자세한 내용 .
방출 섹션위 GitHub 리포지토리 은이 MsvcLibX 라이브러리를 사용하는 여러 프로그램을 제공하며이 기능은 해당 기능을 보여줍니다. 예 : PATH에 ASCII가 아닌 이름을 가진 디렉토리를 가진 which.exe 도구를 사용하고, ASCII가 아닌 이름을 가진 프로그램을 검색하고 코드 페이지를 변경하십시오.
또 다른 유용한 도구로는 conv.exe 프로그램이 있습니다. 이 프로그램은 모든 코드 페이지에서 다른 스트림으로 데이터 스트림을 쉽게 변환 할 수 있습니다. 기본값은 Windows 코드 페이지에 입력되고 현재 콘솔 코드 페이지에 출력됩니다. 이를 통해 다음과 같은 간단한 명령을 사용하여 명령 콘솔에서 Windows GUI 앱 (예 : 메모장)으로 생성 된 데이터를 올바르게 볼 수 있습니다.type WINFILE.txt | conv
이 MsvcLibX 라이브러리는 결코 완전한 것은 아니며,이를 개선하기위한 기여도 환영합니다!