在 PERL 中正确输出特殊字符(unicode)

tak*_*uji 5 regex unicode perl character

我试图获取目录中的所有文件名并确定哪些名称包含特殊字符。我正在使用正则表达式

/[^-a-zA-Z0-9_.]/
Run Code Online (Sandbox Code Playgroud)

示例文件(我使用 touch 创建):

pdf-2014à014&7_06_64-Os_O&L,_Inc.pdf
pdf-20_06_04-O_OnLine,_Inc.pdf
pdf-20_0_0-Utà_d.wr.pdf
pdf-20_12_28-20.Mga_Grf.Fwd_Notice_KDJFI789&_JFK38.pdf
pdf-2_0_0-C_—_DUKE.pdf
pdf-2_1_3-f_s-M_F_D&A.pdf
pdf_-_2014à014&1007_0617_06264-O_O&L,_Inc.pdf
Run Code Online (Sandbox Code Playgroud)

在我匹配正则表达式中模式的名称之前,Perl 可以输出一次正确的名称。是的,perl 以某种方式能够匹配特殊字符,但是在输出字符更改时。

* pdf-2_0_0-C_—_DUKE.pdf          >        pdf-2_0_0-C_???_DUKE.pdf
Run Code Online (Sandbox Code Playgroud)

我可以尝试取消注释这一行

   #binmode(STDOUT, ":utf8");
Run Code Online (Sandbox Code Playgroud)

并再次运行命令脚本。确定吗?标记将被删除,但输出也不同。

* pdf-2_0_0-C_—_DUKE.pdf          >        pdf-2_0_0-C_â_DUKE.pdf
Run Code Online (Sandbox Code Playgroud)

这是我的代码:

use strict;
use warnings;
use File::Find;
use Cwd;

#binmode(STDOUT, ":utf8");

my $starting_directory = cwd();

use Term::ANSIColor;

checkForSpecialChar(cwd());


sub checkForSpecialChar{
    my ($source_dir) = @_;

    chdir $source_dir or die qq(Cannot change into "$source_dir");

    find ( sub {
        return unless -f;   #We want files only
        print "\n";
        while(m/([^-a-zA-Z0-9_.])/g){ 
            chomp($_);
            print "DETECTED: |" . $_ . "|\n";
            print $`;
            print color 'bold red';
            print "$1";
            print color 'reset';
            print  $' . "\n";

        }

    }, ".");

    chdir("$starting_directory");
Run Code Online (Sandbox Code Playgroud)

任何想法家伙?

更新:嗯,你们是对的,看起来它不是正则表达式的问题。嗨 AKHolland,我尝试将代码更改为与您的测试一样。但仍然会产生与 hypen 和小写字母 a-grave 相同的问题。当不使用 binmode(STDOUT, ":utf8"); 时,它不是小写字母 a-grave,而是给我一个` aÌ 使用 binmode(STDOUT, ":utf8");

use strict;
use warnings;
use File::Find;
use Cwd;
use Encode;
binmode(STDOUT, ":utf8");

my $starting_directory = cwd();

use Term::ANSIColor;

checkForSpecialChar(cwd());


sub checkForSpecialChar{
   my ($source_dir) = @_;

   chdir $source_dir
       or die qq(Cannot change into "$source_dir");

   find ( sub {
      return unless -f;   #We want files only
     print $_ . "\n";
      $_ = Encode::decode_utf8($_);
      for(my $counter =0; $counter < length($_); $counter++) {
        print Encode::encode_utf8(substr($_,$counter,1)) .  "\n";
      } 

}, ".");
Run Code Online (Sandbox Code Playgroud)

chdir("$starting_directory"); }

输出与

    二进制模式(标准输出,“:utf8”);

pdf-2_0_0-C_â_DUKE.pdf
磷
d
F
——
2
_
0
_
0
——
C
_
一种
_
D
你
钾
乙
.
磷
d
F
pdf_-_2014aÌ014&1007_0617_06264-O_O&L,_Inc.pdf
磷
d
F
_
——
_
2
0
1
4
一种
一世
0
1
4
&
1
0
0
7
_
0
6
1
7
_
0
6
2
6
4
——
哦
_
哦
&
升
,
_
一世
n
C
.
磷
d
F
输出无 
    二进制模式(标准输出,“:utf8”);

pdf-2_0_0-C_—_DUKE.pdf
磷
d
F
——
2
_
0
_
0
——
C
_
—
_
D
你
钾
乙
.
磷
d
F
pdf_-_2014à014&1007_0617_06264-O_O&L,_Inc.pdf
磷
d
F
_
——
_
2
0
1
4
一种
?
0
1
4
&
1
0
0
7
_
0
6
1
7
_
0
6
2
6
4
——
哦
_
哦
&
升
,
_
一世
n
C
.
磷
d
F

AKH*_*and 3

您需要在传入时对其进行解码,并在传出时对其进行编码。像这样的东西:

use Encode;
find ( sub {
    $_ = Encode::decode_utf8($_);
    while(m/([^-a-zA-Z0-9_.])/g){
        my $chr = Encode::encode_utf8($1);
        print "$chr\n"
    }
}, ".");
Run Code Online (Sandbox Code Playgroud)

  • 是的,文件名应该被视为二进制数据,因此需要对它们进行解码和编码。Linux 倾向于使用 UTF-8 作为文件名,而 Windows 在其 NTFS 文件系统中使用 UTF-16。Perl 的“Encode”模块为此提供了通用的“encode”和“decode”函数。 (2认同)