When I open cmd.exe in Windows, what encoding is it using?
How can I check which encoding it is currently using? Does it depend on my regional setting or are there any environment variables to check?
What happens when you type a file with a certain encoding? Sometimes I get garbled characters (incorrect encoding used) and sometimes it kind of works. However I don't trust anything as long as I don't know what's going on. Can anyone explain?
Solution 1
Yes, its frustratingsometimes type and other programs
print gibberish, and sometimes they do not.
First of all, Unicode characters will only display if the current console font contains the characters. So use a TrueType font like Lucida Console instead of the default Raster Font.
But if the console font doesnt contain the character youre trying to display, youll see question marks instead of gibberish. When you get gibberish, theres more going on than just font settings.
When programs use standard C-library I/O functions like printf, the
programs output encoding must match the consoles output encoding, or
you will get gibberish. chcp shows and sets the current codepage. All
output using standard C-library I/O functions is treated as if it is in the
codepage displayed by chcp.
Matching the programs output encoding with the consoles output encoding can be accomplished in two different ways:
A program can get the consoles current codepage using
chcporGetConsoleOutputCP, and configure itself to output in that encoding, orYou or a program can set the consoles current codepage using
chcporSetConsoleOutputCPto match the default output encoding of the program.
However, programs that use Win32 APIs can write UTF-16LE strings directly
to the console with
WriteConsoleW.
This is the only way to get correct output without setting codepages. And
even when using that function, if a string is not in the UTF-16LE encoding
to begin with, a Win32 program must pass the correct codepage to
MultiByteToWideChar.
Also, WriteConsoleW will not work if the programs output is redirected;
more fiddling is needed in that case.
type works some of the time because it checks the start of each file for
a UTF-16LE Byte Order Mark
(BOM), i.e. the bytes 0xFF 0xFE.
If it finds such a
mark, it displays the Unicode characters in the file using WriteConsoleW
regardless of the current codepage. But when typeing any file without a
UTF-16LE BOM, or for using non-ASCII characters with any command
that doesnt call WriteConsoleWyou will need to set the
console codepage and program output encoding to match each other.
How can we find this out?
Heres a test file containing Unicode characters:
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK
Heres a Java program to print out the test file in a bunch of different
Unicode encodings. It could be in any programming language; it only prints
ASCII characters or encoded bytes to 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();
}
}
}
}
The output in the default codepage? Total garbage!
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|DB
R u s s i a n 0123456 MNO
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|DB
R u s s i a n 0123456 MNO
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|DB
R u s s i a n 0123456 MNO
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|DB
R u s s i a n 0123456 MNO
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}
However, what if we type the files that got saved? They contain the exact
same bytes that were printed to the console.
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|DB
R u s s i a n 0123456 MNO
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|DB
R u s s i a n 0123456 MNO
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|DB
R u s s i a n 0123456 MNO
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 õ¢áÕÑ¢
The only thing that works is UTF-16LE file, with a BOM, printed to the
console via type.
If we use anything other than type to print the file, we get garbage:
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|DB
R u s s i a n 0123456 MNO
C J K `O}Y
1 file(s) copied.
From the fact that copy CON does not display Unicode correctly, we can
conclude that the type command has logic to detect a UTF-16LE BOM at the
start of the file, and use special Windows APIs to print it.
We can see this by opening cmd.exe in a debugger when it goes to type
out a file:
After type opens a file, it checks for a BOM of 0xFEFFi.e., the bytes
0xFF 0xFE in little-endianand if there is such a BOM, type sets an
internal fOutputUnicode flag. This flag is checked later to decide
whether to call WriteConsoleW.
But thats the only way to get type to output Unicode, and only for files
that have BOMs and are in UTF-16LE. For all other files, and for programs
that dont have special code to handle console output, your files will be
interpreted according to the current codepage, and will likely show up as
gibberish.
You can emulate how type outputs Unicode to the console in your own programs like so:
#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;
}
This program works for printing Unicode on the Windows console using the default codepage.
For the sample Java program, we can get a little bit of correct output by setting the codepage manually, though the output gets messed up in weird ways:
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
However, a C program that sets a Unicode UTF-8 codepage:
#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;
}
does have correct output:
Z:\andrew\projects\sx\1259084>.\test
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK
The moral of the story?
typecan print UTF-16LE files with a BOM regardless of your current codepage- Win32 programs can be programmed to output Unicode to the console, using
WriteConsoleW. - Other programs which set the codepage and adjust their output encoding accordingly can print Unicode on the console regardless of what the codepage was when the program started
- For everything else you will have to mess around with
chcp, and will probably still get weird output.
Solution 2
Type
chcp
to see your current code page (as Dewfy already said).
Use
nlsinfo
to see all installed code pages and find out what your code page number means.
You need to have Windows Server 2003 Resource kit installed (works on Windows XP) to use nlsinfo.
Solution 3
To answer your second query re. how encoding works, Joel Spolsky wrote a great introductory article on this. Strongly recommended.
Solution 4
Command CHCP shows the current codepage. It has three digits: 8xx and is different from Windows 12xx. So typing a English-only text you wouldn't see any difference, but an extended codepage (like Cyrillic) will be printed wrongly.
Solution 5
I've been frustrated for long by Windows code page issues, and the C programs portability and localisation issues they cause. The previous posts have detailed the issues at length, so I'm not going to add anything in this respect.
To make a long story short, eventually I ended up writing my own UTF-8 compatibility library layer over the Visual C++ standard C library. Basically this library ensures that a standard C program works right, in any code page, using UTF-8 internally.
This library, called MsvcLibX, is available as open source at https://github.com/JFLarvoire/SysToolsLib. Main features:
- C sources encoded in UTF-8, using normal char[] C strings, and standard C library APIs.
- In any code page, everything is processed internally as UTF-8 in your code, including the main() routine argv[], with standard input and output automatically converted to the right code page.
- All stdio.h file functions support UTF-8 pathnames > 260 characters, up to 64 KBytes actually.
- The same sources can compile and link successfully in Windows using Visual C++ and MsvcLibX and Visual C++ C library, and in Linux using gcc and Linux standard C library, with no need for #ifdef ... #endif blocks.
- Adds include files common in Linux, but missing in Visual C++. Ex: unistd.h
- Adds missing functions, like those for directory I/O, symbolic link management, etc, all with UTF-8 support of course :-).
More details in the MsvcLibX README on GitHub, including how to build the library and use it in your own programs.
The release section in the above GitHub repository provides several programs using this MsvcLibX library, that will show its capabilities. Ex: Try my which.exe tool with directories with non-ASCII names in the PATH, searching for programs with non-ASCII names, and changing code pages.
Another useful tool there is the conv.exe program. This program can easily convert a data stream from any code page to any other. Its default is input in the Windows code page, and output in the current console code page. This allows to correctly view data generated by Windows GUI apps (ex: Notepad) in a command console, with a simple command like: type WINFILE.txt | conv
This MsvcLibX library is by no means complete, and contributions for improving it are welcome!
Solution 6
In Java I used encoding "IBM850" to write the file. That solved the problem.
