带度数(°)的printf对齐问题

时间:2019-07-12 22:05:13

标签: perl utf-8 character-encoding printf

我遇到类似于How can I properly align UTF-8 strings with Perl's printf?的问题:

我(Linux)系统的区域设置默认为LC_CTYPE=de_DE.UTF-8,我编写了一个Perl程序(使用perl-5.26.1),该程序“不是”使用Unicode字符,而是使用ISO Latin-1字符集(例如°)。 因此,我没有在Perl脚本中激活任何Unicode或语言环境功能。

“所有内容”似乎都可以正常运行,其中一个例外是:我正在使用printf的{​​{1}}格式来对齐字符串,但这不能按预期工作。

在调试器中播放时,会出现以下现象:

%-10s

到目前为止看起来还不错...

  DB<1> $s='X°X'

  DB<2> printf("_%3s_\n", $s)
_X°X_

糟糕;那不是 DB<3> printf("_%4s_\n", $s) _X°X_ 吗?

"_ X°X_"

一个人掉吗?

  DB<4> printf("_%5s_\n", $s)
_ X°X_

那不是 DB<5> x length($s) 0 4 吗?

3

DB<8> x ord($s[1]) 0 0 DB<9> x $s 0 'X°X' DB<10> 是否应编码为一个字节?我以为UTF-8会将未修改的Latin-1范围映射到Unicode。

所以可能的问题是:

  1. 发生了什么事?

  2. 这是Perl的错误吗?

  3. 如果没有,如何确定格式和字符串长度?

2 个答案:

答案 0 :(得分:3)

UTF-8仅将ASCII范围(0..127)映射到1个字节。 Latin-1字符的范围是0..255; UTF-8 不能将它们全部映射到一个字节。如果是这样,将没有其他任何映射。

0到127之间的字符被编码为1个字节。
从128到2047的字符被编码为2个字节。
依此类推。

https://en.wikipedia.org/wiki/UTF-8

您需要在Perl脚本中使用use utf8;binmode STDOUT, ':encoding(UTF-8)';(为了保持一致性,我对STDINSTDERR进行了同样的操作:

#!/usr/bin/perl

use strict;
use warnings;
use utf8;

BEGIN {
    binmode STDIN,  ':encoding(UTF-8)';
    binmode STDOUT, ':encoding(UTF-8)';
    binmode STDERR, ':encoding(UTF-8)';
}

printf "|%-10s|\n", "x";
printf "|%-10s|\n", "°";

输出正确对齐:

|x         |
|°         |

如果我注释掉use utf8;binmode STDOUT, ':encoding(UTF-8)';,则输出未对齐和/或度数字符显示不正确。

引用perldoc utf8utf8模块的文档):

  

use utf8”指示编译器允许Perl解析器中的UTF-8   当前词法范围内的程序文本。

(这需要将输出设备或终端仿真器配置为显示UTF-8。)

答案 1 :(得分:2)

Perl代码必须使用ASCII(默认为{no utf8;)或UTF-8(use utf8;)编码。

°不在ASCII字符集中,而且您显然也没有use utf8;,因此您的程序可能无法如您所愿地包含°

首先,使用UTF-8(如果尚未编码)对程序进行编码,然后通过添加

告诉Perl您的程序已使用UTF-8编码

use utf8;   # The source code is encoded using UTF-8.

第二,您显然也没有告诉Perl对您打印的内容进行编码。通过添加

来解决此问题
use open ':std', ':encoding(UTF-8)';   # The terminal provides/expects UTF-8.

后者为在编译指示范围内打开的文件设置默认编码。如果要避免这种情况,可以改用以下内容:

BEGIN {   # The terminal provides/expects UTF-8.
   binmode(STDIN,  ':encoding(UTF-8)');
   binmode(STDOUT, ':encoding(UTF-8)');
   binmode(STDERR, ':encoding(UTF-8)');
}