"Wide character in print"¶
Level: 101 · you have seen the warning — or worse, garbage with no warning
One line: A filehandle with no encoding layer writes a string in perl's internal form, so café goes out as the single byte e9 — not UTF-8, and no warning — unless the same string contains any character above U+00FF, in which case the whole string goes out as UTF-8 with a "Wide character" warning; one layer on the handle makes both right.
Measured¶
Every command's output is piped through hex, a byte dump written in perl, so the bytes are the evidence and the terminal's rendering is not:
Verified output of wide_character_sh.sh — regenerated by tools/run_examples.py, never hand-typed.
$ perl -e 'print "caf\x{E9}\n"' | hex
63 61 66 e9 0a
$ perl -e 'print "caf\x{E9} \x{2192} ok\n"' 2>warnings.txt | hex; cat warnings.txt
63 61 66 c3 a9 20 e2 86 92 20 6f 6b 0a
Wide character in print at -e line 1.
$ perl -e 'print "caf\x{E9}\n", "caf\x{E9} \x{2192}\n"' 2>/dev/null | hex
63 61 66 e9 0a 63 61 66 c3 a9 20 e2 86 92 0a
$ perl -CS -e 'print "caf\x{E9} \x{2192} ok\n"' 2>warnings.txt | hex; cat warnings.txt
63 61 66 c3 a9 20 e2 86 92 20 6f 6b 0a
$ perl -e 'binmode STDOUT, ":encoding(UTF-8)"; print "caf\x{E9}\n"' | hex
63 61 66 c3 a9 0a
$ perl -e 'use open qw(:std :encoding(UTF-8)); print "caf\x{E9} \x{2192} ok\n"' 2>warnings.txt | hex; cat warnings.txt
63 61 66 c3 a9 20 e2 86 92 20 6f 6b 0a
Reading the bytes¶
caf\x{E9}came out as63 61 66 e9. Perl stores a string whose characters all fit below U+0100 at one byte per character, and with no layer it writes that storage.e9alone is not valid UTF-8, so a UTF-8 terminal or editor shows a replacement mark — and there was no warning, because perl had no trouble writing it.- Add
→(U+2192) and it cannot fit in a byte, so perl writes the string's other internal form, UTF-8:ébecomesc3 a9, the arrowe2 86 92, and perl warnsWide character in print. - Both in one
print:e9in the first string,c3 a9in the second. The same character, as two different byte sequences, in one line of output. A program that prints data this way writes files that are neither Latin-1 nor UTF-8, and which one a line is depends on what else was on it. -CS,binmode STDOUT, ":encoding(UTF-8)"anduse open qw(:std :encoding(UTF-8))each put an encoding layer on the handle. Every string is encoded as UTF-8 on the way out,éis alwaysc3 a9, and the warnings file stays empty.
The warning is the good case. It appears only when perl had to guess — and its guess, UTF-8, is usually right. The silent e9 is the one that corrupts a file.
What to do instead¶
In a program, use open qw(:std :encoding(UTF-8)); at the top, and a layer on every handle you open yourself:
In a one-liner, -CS, or -CSDA to decode input and @ARGV as well. And do not silence the warning: it is telling you that a handle has no layer, which means some other line through the same handle is already wrong.
If you are coming from another language¶
- Python.
printalways encodes, withsys.stdout.encoding, and a character that encoding cannot hold is an error rather than a guess. See Standard in, standard out, and pipes ↗. - Java.
System.outencodes with the console's charset and turns what it cannot encode into?, silently. See The default charset ↗. - Rust. A
&stris UTF-8 andprintln!writes its bytes; there is no second form to fall back to. See Six kinds of string ↗. - The encodings library.
printfwrites bytes ↗ and Reading a hex dump ↗.
See also¶
use utf8is about the source file — the same layer, and the double encoding you get when the source was not decoded- Decode at the edges — the input half