为什么这个行编号命令会破坏字符编码?

时间:2013-01-02 17:19:08

标签: linux perl encoding

我想通过在每行的开头添加行号来修改文件。我发现以下命令执行此操作:

cat file | perl -pe '$_ = "$. $_"' > file_with_line_numbers

然而,这似乎有效,当我在vim中打开文件时,它充满了^ @和^ M个字符。进一步的调查显示编码已经改变。

> file -bi file
text/plain; charset=utf-16le

> file -bi file_with_line_numbers
application/octet-stream; charset=binary

我在这里缺少什么?

2 个答案:

答案 0 :(得分:9)

因为您没有对输入数据进行解码,而且您没有对输出数据进行编码,并且通过将$.$_连接起来,您混合了两种不同编码的数据(相反,你正在混合一个字节字符串和一个字符串,但是perl会隐式地将字节字符串转换为字符串,并以非常错误的方式为你所需要的方式进行操作。

一个解决方案是:

perl -pe  'BEGIN { binmode STDIN, ":encoding(utf16le)"; binmode STDOUT, ":encoding(utf16le)" } $_ = "$. $_";' < input > output

答案 1 :(得分:5)

您需要解码程序的输入并对程序的输出进行编码。

正如ysth所指出的,这将起到作用(除了在Windows上,但可能使用cygwin):

perl -Mopen=:std,':encoding(utf-16le)' -pe'$_="$. $_";' file.in >file.out

其余原始答案:

如果你有UTF-8,这是最容易的,因为你可以使用-CSDA

<file.in iconv -f UTF-16le -t UTF-8 \
   | perl -CSDA -pe'$_="$. $_";' \
     | iconv -f UTF-8 -t UTF-16le \
       >file.out

由于UTF-8的属性,在这种情况下,您可以完全脱离而无需解码/编码,允许您使用以下任一项:

<file.in iconv -f UTF-16le -t UTF-8 \
   | perl -pe'$_="$. $_";' \
     | iconv -f UTF-8 -t UTF-16le \
       >file.out

<file.in iconv -f UTF-16le -t UTF-8 \
   | nl \
     | iconv -f UTF-8 -t UTF-16le \
       >file.out