regex - 如何更改正则表达式以读取 UTF-8?

标签 regex perl utf-8

我正在编写一个脚本,但发现它在阅读 UTF-8 时出现问题。人物。

我在瑞典有一个联系人,他发了 VM在他的机器上,里面有一些 UTF-8,当我的脚本命中那个 VM 时,它失去了理智,但它能够读取“正常”字符集中的所有其他 VM。

无论如何,也许我的代码会更有意义。

#!/usr/bin/perl
use strict;
use warnings;
#use utf8;
use Net::OpenSSH;

# Create a hash for storing the options needed by Net::OpenSSH
my %ssh_options = (
    port => '22',
    user => 'root',
    password => 'password'
);

# Create a new Net::OpenSSH object
my $ssh = Net::OpenSSH->new('192.168.2.101', %ssh_options);

# Create an array and capture the ESX\ESXi output from the current server
my @getallvms = $ssh->capture('vim-cmd vmsvc/getallvms');
shift @getallvms;
# Process data gathered from server
foreach my $vm (@getallvms) {
    # Match ID, NAME
    $vm =~  m/^(?<id> \d+)\s+(?<name> .+?)\s+/xm;
    my $id = "$+{id}";
    my $name = "$+{name}";
    print "$id\n";
    print "$name\n";
    print "\n";
}

我已将问题缩小到我的正则表达式,因为这里是应用正则表达式之前服务器的原始输出。
416
TEST Box åäö!"''*#

这就是我应用正则表达式后得到的
416
TEST

由于某种原因,正则表达式不匹配,我只是不知道为什么。示例中的当前正则表达式是使其工作的第三次尝试。

我匹配的 FULL 行看起来像这样。我的正则表达式完成的方式是因为我只需要前两个信息 block ,你想要复制整行的表达式。

编码:
432    TEST Box åäö!"''*#   [Store] TEST Box +w6XDpMO2IQ-_''_+Iw/TEST Box +w6XDpMO2IQ _''_+Iw.vmx   slesGuest    vmx-04

最佳答案

子模式

(?<name> .+?)\s+

在你的正则表达式中的意思是“匹配并记住一个或多个非换行符,但一旦找到空格就停止”,所以 $name包含 TEST因为模式在看到 Box 之前的空格时停止匹配.

VI Toolkit wiki给出 example getallvms 子命令的输出:
# vmware-vim-cmd -H 10.10.10.10 -U root -P password /vmsvc/getallvms
Vmid    Name               File                 Guest OS       Version   Annotation
64     bartPE    [store] BartPE/BartPE.vmx     winXPProGuest     vmx-04
96     trustix   [store] Trustix/Trustix.vmx   otherLinuxGuest   vmx-04

The case is slightly different from the example in your question, but it appears that we can look for [store] as a bumper for the match:

/^(?<id> \d+) \s+ (?<name> .+?) \s+ \[store]/mix

不贪心quantifier +?表示匹配一项或多项内容,但匹配项希望尽快将控制权交给模式的其余部分。请记住 [在正则表达式中有特殊含义,但模式 \[匹配文字而不是引入字符类。

我认为这种技术是装订或固定和拉伸(stretch)。如果您想提取一段难以表征的文本,请寻找易于匹配的周围特征——通常就像 ^ 一样简单。或 $ .然后使用有弹性的图案捕获中间的所有东西,通常是 (.+)(.+?) .阅读 “Quantifiers” section of the perlre documentation了解您的许多选项。

这解决了眼前的问题,您还可以在一些区域添加润色。

不要使用 $1 , $2 ,和 friend 无条件!在使用捕获变量之前始终测试模式是否匹配。例如
if (/(foo|bar|baz)/) {
  print "got $1\n";
}
else {
  print "no match\n";
}

未 protected print $1可以产生难以调试的令人惊讶的结果。

明智地使用 Perl 的默认值有助于强调计算并使机制淡入后台。丢弃$vm赞成$_因为隐式循环变量和隐式匹配目标会产生更好的结果。

您的评论只是从 Perl 翻译成英文。最有用的评论解释了原因,而不是什么。还要记住 Rob Pike 的 advice on commenting :

If your code needs a comment to be understood, it would be better to rewrite it so it's easier to understand.



%+ 的作业中,引号没有做任何有用的事情。这些值已经是字符串,因此请删除引号。
my $id   = $+{id};
my $name = $+{name};

下面是您的代码的修改版本,它捕获了数字之后但 [store] 之前的所有内容进入 $name . utf8 pragma声明您的源代码(不是像常见错误那样,您的输入)包含 UTF-8。下面的测试模拟了一个 jar 头 echo vim-cmd 的输出在瑞典 VM 上。

正如汤姆建议的那样,我使用 Encode模块对通过 SSH 连接到达的输出进行解码,并在打印输出之前对其进行编码以利于本地主机。

perlunifaq文档建议将外部数据解码为 Perl 的内部格式,然后在写入之前对任何输出进行编码。我假设从 $ssh->capture(...) 返回的值使用 UTF-8 编码,即远程主机发送的是 UTF-8。我们看到了预期的结果,因为我正在运行 Linux 的现代发行版并通过 ssh-ing 回到它,但在野外,您可能正在处理一些其他编码。

您可以跳过对 decode 的调用而侥幸逃脱。和 encode因为 Perl 的内部格式恰好与您正在使用的主机匹配。然而,一般来说,偷工减料会给你带来麻烦:
  • What if I don't decode?
  • What if I don't encode?

  • 最后,代码!
    #! /usr/bin/env perl
    
    use strict;
    use utf8;
    use warnings;
    
    use Encode;
    use Net::OpenSSH;
    
    my %ssh_options = ();
    my $ssh = Net::OpenSSH->new('localhost', %ssh_options);
    
    # Create an array and capture the ESX\ESXi output from the current server
    #my @getallvms = $ssh->capture('vim-cmd vmsvc/getallvms');
    my @getallvms = $ssh->capture(<<EOEcho);
    echo -e 'JUNK\n416 TEST Box åäö!"'\\'\\''*#    [Store] TEST Box +w6XDpMO2IQ-_''_+Iw/TEST Box +w6XDpMO2IQ _''_+Iw.vmx   slesGuest    vmx-04'
    EOEcho
    shift @getallvms;
    
    for (@getallvms) {
      $_ = decode "utf8", $_, Encode::FB_CROAK;
    
      if (/^(?<id> \d+) \s+ (?<name> .+?) \s+ \[store]/mix) {
        my $id   = $+{id};
        my $name = $+{name};
        print encode("utf8", $id),   "\n",
              encode("utf8", $name), "\n",
              "\n";
      }
      else {
        print "no match\n";
      }
    }
    

    输出:

    416
    测试盒 åäö!"''*#

    关于regex - 如何更改正则表达式以读取 UTF-8?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4933703/

    相关文章:

    regex - 使用正则表达式检测和转换字符串中的数字

    r - R 中非 ASCII 字符的正向后视

    java - 正则表达式不匹配由空格分隔的单词

    perl - Moo、惰性属性和默认/强制调用

    python - 转义 Django 模板中的特殊字符

    ruby - 如何解码通过 Net::IMAP 在 UTF8 中获取的主题? ( ruby )

    encoding - 错误的 MySQL 导入,现在我们用垃圾代替 utf-8 字符显示

    java - 匹配非多行正则表达式

    perl - 我应该以Web开发人员的身份学习Perl吗?

    perl - SSLeay.so 中 undefined symbol : OpenSSL_add_all_algorithms