如何在Perl中删除不可打印字符^@?

3
我有一个简单的perl脚本如下:
use strict; 
use warnings 'all';
use Text::CSV;
use open ":std", ":encoding(UTF-8)";
my $in_file = $ARGV[0] or die "Usage: $0 filename\n";
my $out_file = $ARGV[1] or die "Usage: $0 filename\n";
my $csv = Text::CSV->new( { binary => 1, auto_diag => 1 }); 
my $out_csv = Text::CSV->new ( { binary => 1, 
                auto_diag => 1, 
                eol => $/,
                    sep_char => ',', 
                quote_char => '"', 
                always_quote => 1});
open my $data,   '<:encoding(UTF-8)', $in_file or die $!; 
open my $fh_out, '>:encoding(UTF-8)', $out_file or die $!; 

while (my $words = $csv->getline($data))  
{ 
    tr/\r\n//d for @$words;
    tr/,/;/ for @$words;
    tr/"/'/ for @$words;
    $out_csv->print($fh_out, $words);
} 

它的作用是将CSV文件转换为更结构化的格式,以便外部应用程序可以轻松理解,而外部应用程序有其限制。该脚本删除用户文本输入中输入的不必要的换行符、逗号和双引号。这一直运作良好。然而,最近在一个输入文件中出现了非打印字符^@,脚本并没有失败,但输出文件会多出额外的字符"0。

由于该字符属于非打印字符,因此不确定能否提供输入文件的示例,请参见以下命令输出。

cat Input.csv 
ObjectId,OrgId,Title,ObjectType,Type,ObjectId,Location,StartDate,EndDate,DueDate,ObjectId1,ObjectId2,ObjectId3,LastModified,IsDeleted,SortOrder,Depth,DummyId,IsHidden,ResultId,DeletedDate,CreatedBy,LastModifiedBy,DeletedBy
3386484,532947,Test,Topic,Auto,3386415,http://www.test.com ,,,,,,,2016-06-27T05:08:26.3070000Z,1,443,3,,False,,2017-02-16T00:31:39.4870000Z,,,

使用cat -e选项可以查看不可打印字符。

cat -e Input.csv 
M-oM-;M-?ObjectId,OrgId,Title,ObjectType,Type,ObjectId,Location,StartDate,EndDate,DueDate,ObjectId1,ObjectId2,ObjectId3,LastModified,IsDeleted,SortOrder,Depth,DummyId,IsHidden,ResultId,DeletedDate,CreatedBy,LastModifiedBy,DeletedBy^M$
3386484,532947,Test,Topic,Auto,3386415,http://www.test.com ^@,,,,,,,2016-06-27T05:08:26.3070000Z,1,443,3,,False,,2017-02-16T00:31:39.4870000Z,,,^M$


文件经过脚本后的输出如下。在http://www.test.com末尾增加了一个“0”。
cat -e Output.csv 
"M-oM-;M-?ObjectId","OrgId","Title","ObjectType","Type","ObjectId","Location","StartDate","EndDate","DueDate","ObjectId1","ObjectId2","ObjectId3","LastModified","IsDeleted","SortOrder","Depth","DummyId","IsHidden","ResultId","DeletedDate","CreatedBy","LastModifiedBy","DeletedBy"$
"3386484","532947","Test","Topic","Auto","3386415","http://www.test.com "0","","","","","","","2016-06-27T05:08:26.3070000Z","1","443","3","","False","","2017-02-16T00:31:39.4870000Z","","",""$

根据Dave的要求添加命令输出

od -ch Input.csv

0000000 357 273 277   O   b   j   e   c   t   I   d   ,   O   r   g   I
           bbef    4fbf    6a62    6365    4974    2c64    724f    4967
0000020   d   ,   T   i   t   l   e   ,   O   b   j   e   c   t   T   y
           2c64    6954    6c74    2c65    624f    656a    7463    7954
0000040   p   e   ,   T   y   p   e   ,   O   b   j   e   c   t   I   d
           6570    542c    7079    2c65    624f    656a    7463    6449
0000060   ,   L   o   c   a   t   i   o   n   ,   S   t   a   r   t   D
           4c2c    636f    7461    6f69    2c6e    7453    7261    4474
0000100   a   t   e   ,   E   n   d   D   a   t   e   ,   D   u   e   D
           7461    2c65    6e45    4464    7461    2c65    7544    4465
0000120   a   t   e   ,   O   b   j   e   c   t   I   d   1   ,   O   b
           7461    2c65    624f    656a    7463    6449    2c31    624f
0000140   j   e   c   t   I   d   2   ,   O   b   j   e   c   t   I   d
           656a    7463    6449    2c32    624f    656a    7463    6449
0000160   3   ,   L   a   s   t   M   o   d   i   f   i   e   d   ,   I
           2c33    614c    7473    6f4d    6964    6966    6465    492c
0000200   s   D   e   l   e   t   e   d   ,   S   o   r   t   O   r   d
           4473    6c65    7465    6465    532c    726f    4f74    6472
0000220   e   r   ,   D   e   p   t   h   ,   D   u   m   m   y   I   d
           7265    442c    7065    6874    442c    6d75    796d    6449
0000240   ,   I   s   H   i   d   d   e   n   ,   R   e   s   u   l   t
           492c    4873    6469    6564    2c6e    6552    7573    746c
0000260   I   d   ,   D   e   l   e   t   e   d   D   a   t   e   ,   C
           6449    442c    6c65    7465    6465    6144    6574    432c
0000300   r   e   a   t   e   d   B   y   ,   L   a   s   t   M   o   d
           6572    7461    6465    7942    4c2c    7361    4d74    646f
0000320   i   f   i   e   d   B   y   ,   D   e   l   e   t   e   d   B
           6669    6569    4264    2c79    6544    656c    6574    4264
0000340   y  \r  \n   3   3   8   6   4   8   4   ,   5   3   2   9   4
           0d79    330a    3833    3436    3438    352c    3233    3439
0000360   7   ,   T   e   s   t   ,   T   o   p   i   c   ,   A   u   t
           2c37    6554    7473    542c    706f    6369    412c    7475
0000400   o   ,   3   3   8   6   4   1   5   ,   h   t   t   p   :   /
           2c6f    3333    3638    3134    2c35    7468    7074    2f3a
0000420   /   w   w   w   .   t   e   s   t   .   c   o   m      \0   ,
           772f    7777    742e    7365    2e74    6f63    206d    2c00
0000440   ,   ,   ,   ,   ,   ,   2   0   1   6   -   0   6   -   2   7
           2c2c    2c2c    2c2c    3032    3631    302d    2d36    3732
0000460   T   0   5   :   0   8   :   2   6   .   3   0   7   0   0   0
           3054    3a35    3830    323a    2e36    3033    3037    3030
0000500   0   Z   ,   1   ,   4   4   3   ,   3   ,   ,   F   a   l   s
           5a30    312c    342c    3334    332c    2c2c    6146    736c
0000520   e   ,   ,   2   0   1   7   -   0   2   -   1   6   T   0   0
           2c65    322c    3130    2d37    3230    312d    5436    3030
0000540   :   3   1   :   3   9   .   4   8   7   0   0   0   0   Z   ,
           333a    3a31    3933    342e    3738    3030    3030    2c5a
0000560   ,   ,  \r  \n
           2c2c    0a0d
0000564


我该如何在脚本中处理这个问题,我不希望输出的CSV中有额外的“0”。
谢谢。

1
请问您能否在问题中添加运行 od -ch Input.csv 的结果? - Dave Cross
3个回答

3

一旦您找到该字符的数字值,您可以使用\x{}来指定它的编码号码:

s/\x{....}//g;

例如,如果字符是,我可以找到它的序数值。我通常使用十六进制转储来实现这一点,但有多种方法。它的Unicode编码是U+1F431:
s/\x{1F431}//g;

您还可以使用八进制指定非宽字符的代码数字:

s/\200//g;

或者在字符类中使用它们的范围:

s/[\200-\377]//g;
s/[\000-\037]//g;
s/[\000-\037\200-\377]//g;

但是,你可能还想做另一件事情。你可以通过它具有的属性来匹配它(参见perluniprops):

s/\p{XPosixCntrl}//g

或者,使用大写字母P,它所没有的属性:
s/\P{Print}//g

谢谢@brian。我会尝试这种方法并告诉你结果的。 - AKG

0

所以,从文件的十六进制转储中我们学到了两件事情:

  1. 它绝对是一个UTF8文件 - 因为前三个字符是UTF8字节顺序标记(或BOM)。
  2. 你的问题字符实际上是一个空字符(代码点为零)。

因此,按照Brian的建议,您将能够使用以下方法删除该字符:

s/\0//g;

啊,空字节。你可以安全地从UTF-8字节中删除它们,因为除了null之外的任何字符都不会被编码为使用空字节的序列。 - brian d foy

0

我不确定你是怎么发现它是^@的,但你应该能够通过使用\c@来引用任何这样的东西,其中\c代表"CTRL-"。所以,如果它是SOH,\x01,并且你看到它显示为^A,你可以指定s/\cA//g来摆脱它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接