繁体   English   中英

如何在命令行上处理 utf8(使用 Perl 或 Python)?

[英]How to handle utf8 on the command line (using Perl or Python)?

如何在命令行上使用 Perl(或 Python)处理 utf8?

例如,我正在尝试拆分每个单词中的字符。 这对于非 utf8 文本非常容易,例如:

$ echo "abc def" | perl -ne 'my @letters = m/(.)/g; print "@letters\n"' | less
a b c   d e f

但是对于 utf8 它当然不起作用:

$ echo "одобрение за" | perl -ne 'my @letters = m/(.)/g; print "@letters\n"' | less
<D0> <BE> <D0> <B4> <D0> <BE> <D0> <B1> <D1> <80> <D0> <B5> <D0> <BD> <D0> <B8> <D0> <B5>   <D0> <B7> <D0> <B0>

因为它不知道 2 字节的字符。

如果知道在 Python 中这是如何完成的(即 utf8 的命令行处理),那将是一件好事。

“-C”标志控制一些Perl Unicode功能(请参阅perldoc perlrun ):

$ echo "одобрение за" | perl -C -pe 's/.\K/ /g'
о д о б р е н и е   з а 

要指定用于stdin / stdout的编码,可以使用PYTHONIOENCODING环境变量:

$ echo "одобрение за" | PYTHONIOENCODING=utf-8 python -c'import sys
for line in sys.stdin:
    print " ".join(line.decode(sys.stdin.encoding)),
'
о д о б р е н и е   з а 

如果你想在字符(字形)边界上分割文本(不在代码点上作为上面的代码),那么你可以使用/\\X/正则表达式:

$ echo "одобрение за" | perl -C -pe 's/\X\K/ /g'
о д о б р е н и е   з а 

请参阅Grapheme Cluster Boundaries

在Python \\X中, regex模块支持\\X

“嘿”,我想,“这在Perl中有多难?”

事实证明这很容易。 不幸的是,找出我的时间比我想象的要长。

快速浏览使用utf8告诉我,现在已经过时了。 Perl的binmode看起来很有希望,但并不完全。

发现有一个Perluniintro引导我到Perlunicode说我应该看看Perlrun 然后,我发现了我在找什么。

Perl有一个命令行开关-C ,它将Perl切换为Unicode。 但是, -C命令行开关还需要一些选项。 您需要指定unicode中的内容。 有一个方便的图表 ,显示各种选项。 似乎perl -C本身就没问题了。 它结合了各种选项,相当于-CSDL-C255 但是,这意味着如果您的LOCALE未设置为unicode,则Perl将无法在Unicode中运行。

相反,您应该使用perl -CSD-perl -C63

$ echo "одобрение за" | perl -CSD -ne 'my @letters = m/(.)/g; print "@letters\n"'
о д о б р е н и е   з а

是的,有效。

您只需回答一个问题就可以学到很多东西。

我不知道Perl,所以我正在回答Python。

Python不知道输入文本是Unicode。 您需要从UTF-8或其实际的任何内容显式解码为Unicode。 然后,您可以使用普通的Python文本处理来处理它。

http://docs.python.org/howto/unicode.html

这是一个简单的Python 2.x程序供您尝试:

import sys

for line in sys.stdin:
    u_line = unicode(line, encoding="utf-8")
    for ch in u_line:
        print ch, # print each character with a space after

这将从标准输入中复制行,并将每行转换为Unicode。 编码指定为UTF-8。 然后for ch in u_line中的ch设置ch到每个字符。 然后print ch,是Python 2.x中打印字符的简单方法,后跟空格,没有回车符。 最后一个裸print添加回车。

我仍然使用Python 2.x进行大部分工作,但对于Unicode,我建议您使用Python 3.x. Unicode的东西真的得到了改进。

这是上述程序的Python 3版本,在我的Linux计算机上测试过。

import sys

assert(sys.stdin.encoding == 'UTF-8')
for line in sys.stdin:
    for ch in line:
        print(ch, end=' ') # print each character with a space after

默认情况下,Python 3假定输入编码为UTF-8。 默认情况下,Python然后将其解码为Unicode。 Python 3字符串总是Unicode; 有一个特殊类型的bytes()用于包含非Unicode值(“bytes”)的类似字符串的对象。 这与Python 2.x相反; 在Python 2.x中,基本字符串类型是一个字节字符串,Unicode字符串是一个特殊的新东西。

当然没有必要断言编码是UTF-8,但它是一种很好的简单方法来记录我们的意图并确保默认不会以某种方式改变。

在Python 3中, print()现在是一个函数。 而不是在print语句之后添加逗号以使其打印空格而不是换行符的有点奇怪的语法,现在有一个命名关键字参数,可以让您更改结束字符。

注意:最初我在Python 2.x程序中处理输入行后有一个裸print语句,在Python 3.x程序中有print() 正如JF Sebastian指出的那样,代码是从输入行打印字符,最后一个字符是换行符,因此实际上不需要额外的print语句。

$ echo "одобрение за" | python -c 'import sys, codecs ; x = codecs.
getreader("utf-8")(sys.stdin); print u", ".join(x.read().strip())'
о, д, о, б, р, е, н, и, е,  , з, а

或者如果你想要unicode代码点:

$ echo "одобрение за" | python -c 'import sys, codecs ; x = codecs.
getreader("utf-8")(sys.stdin); print u", ".join("<%04x>" % ord(ch) 
for ch in x.read().strip())'
<043e>, <0434>, <043e>, <0431>, <0440>, <0435>, <043d>, <0438>, 
<0435>, <0020>, <0437>, <0430> 

要使用 Perl 在命令行上处理 UTF-8,我们必须考虑 STDIN、STDOUT、STDERR、arguments 和源代码(作为-e-E选项的参数给出)。

考虑以下测试用例:

echo -n "одобрение за"  | perl -Mstrict -w -E '
  while (<STDIN>){ s/\X\K/ /g; say; }
  say "Arguments and their length:";
  say "  $_\t", length("$_") foreach @ARGV;
  say "Length of 😀 in the source code is ", length("😀");
' a 😀

这是一个很好的测试用例,因为它在 3 个地方有 UTF-8 个编码字符:

  1. 在标准输入上,
  2. 作为 arguments,和
  3. 在源代码中(作为-E选项的参数提供)。

(顺便说一句,我的终端在 UTF-8 语言环境中。)

结果:

� � � � � � � � � � � � � � � � � �   � � � � 
Arguments and their length:
 a  1
 😀 4
Length of 😀 in the source code is 4

首先,让我们去掉问号。 让我们通知 perl 标准流是 UTF-8 编码字符。 为此,请添加-CSD

echo -n "одобрение за"  | perl -Mstrict -w -CSD -E '
  while (<STDIN>){ s/\X\K/ /g; say; }
  say "Arguments and their length:";
  say "  $_\t", length("$_") foreach @ARGV;
  say "Length of 😀 in the source code is ", length("😀");
' a 😀

注意:我可以简单地使用-C因为-C暗示-CSDL在 UTF-8 语言环境的系统上与 -CSD 相同,如-CSD所述

结果:

о д о б р е н и е   з а 
Arguments and their length:
  a 1
  ð 4
Length of ð in the source code is 4

很好,那去掉了问号。

但是现在arguments和源码里面的emoji乱码了。

我们必须通知 perl 我们的 arguments 是 UTF-8。我们通过将 -CSD 更改为-CSD来做到这-CSDA

echo -n "одобрение за"  | perl -Mstrict -w -CSDA -E '
  while (<STDIN>){ s/\X\K/ /g; say; }
  say "Arguments and their length:";
  say "  $_\t", length("$_") foreach @ARGV;
  say "Length of 😀 in the source code is ", length("😀");
' a 😀

结果:

о д о б р е н и е   з а 
Arguments and their length:
 a  1
 😀 1
Length of ð in the source code is 4

好的。 emoji 参数是固定的,它的长度是 1 个字符,正如预期的那样。

源码中的emoji还是有问题的。

通知perl源码编码为UTF-8,添加use utf8; 到源代码或-Mutf8到命令行选项:

echo -n "одобрение за"  | perl -Mutf8 -Mstrict -w -CSDA -E '
  while (<STDIN>){ s/\X\K/ /g; say; }
  say "Arguments and their length:";
  say "  $_\t", length("$_") foreach @ARGV;
  say "Length of 😀 in the source code is ", length("😀");
' a 😀

结果:

о д о б р е н и е   з а 
Arguments and their length:
 a  1
 😀 1
Length of 😀 in the source code is 1

很好,现在我们得到了位于源代码中的 emoji 字符的预期结果。

概括:

  • 添加-CSD以通知 perl 标准流是 UTF-8 编码的。
  • 将其更改为-CSDA以处理 UTF-8 编码的 arguments。
  • 添加use utf8; 添加到源代码或将-Mutf8添加到选项以通知 perl 源代码是 UTF-8 编码的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM