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

ian*_*215 4 regex perl utf-8

我在一个脚本中走得很远,我正在努力发现它在读取UTF-8字符时遇到了问题.

我在瑞典有一个联系人在他的机器上创建了一个虚拟机,其中有一些UTF-8,当我的脚本击中该虚拟机时,它失去了理智,但它能够读取所有其他处于"正常"状态的虚拟机字符集.

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

#!/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";
}
Run Code Online (Sandbox Code Playgroud)

我把它缩小到我的正则表达式作为问题,因为这里是应用正则表达式之前服务器的原始输出.

416
TEST Box åäö!"''*#
Run Code Online (Sandbox Code Playgroud)

这是我应用正则表达式后得到的结果

416
TEST
Run Code Online (Sandbox Code Playgroud)

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

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

代码:

432    TEST Box åäö!"''*#   [Store] TEST Box +w6XDpMO2IQ-_''_+Iw/TEST Box +w6XDpMO2IQ _''_+Iw.vmx   slesGuest    vmx-04
Run Code Online (Sandbox Code Playgroud)

Gre*_*con 7

子模式

(?<name> .+?)\s+
Run Code Online (Sandbox Code Playgroud)

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

VI工具箱维基给出了一个例子的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

这个案例与您的问题中的示例略有不同,但看起来我们可以将其[store]作为匹配的保险杠:

/^(?<id> \d+) \s+ (?<name> .+?) \s+ \[store]/mix
Run Code Online (Sandbox Code Playgroud)

非贪婪量词 +?意味着匹配一个或多个东西,但匹配想要尽快将控制权交给模式的其余部分.请记住,[它在正则表达式中具有特殊含义,但模式\[匹配文字而不是引入字符类.

我认为这种技术是书挡或伸展和伸展.如果要提取难以表征的大量文本,请查找易于匹配的周围特征 - 通常简单^或者$.然后使用弹性图案来抓住两者之间的所有东西,通常(.+)(.+?).阅读perlre文档"Quantifiers"部分,了解您的许多选项.

这解决了眼前的问题,您还可以在一些方面添加润色.

不要无条件地使用$1,$2和朋友!在使用捕获变量之前,始终测试模式是否匹配.例如

if (/(foo|bar|baz)/) {
  print "got $1\n";
}
else {
  print "no match\n";
}
Run Code Online (Sandbox Code Playgroud)

不受保护的print $1可以产生难以调试的令人惊讶的结果.

明智地使用Perl的默认值可以帮助强调计算并让机制淡入背景.滴加$vm赞成$_作为隐含的循环变量和隐式匹配目标使得一个更好的结果.

您的意见仅仅是从Perl翻译成英语.最有帮助的评论解释了为什么,而不是什么.另请记住Rob Pike 关于评论建议:

如果您的代码需要理解注释,那么最好重写它以便更容易理解.

在赋值中%+,引号不起作用.值已经是字符串,因此请删除引号.

my $id   = $+{id};
my $name = $+{name};
Run Code Online (Sandbox Code Playgroud)

下面是您的代码的修改版本,它捕获数字之后但[store]进入之前的所有内容$name.该UTF8编译声明你的源代码 -不是,与一个常见的错误,你的输入中包含UTF-8.下面的测试用一个罐装模拟瑞典VM上的echo输出vim-cmd.

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

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

你能逃脱跳跃到电话decodeencode因为Perl的内部格式恰好匹配您正在使用的主机.但是,一般来说,偷工减料会让你陷入困境:

最后,代码!

#! /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";
  }
}
Run Code Online (Sandbox Code Playgroud)

输出:

416
TEST Box åäö!"''*#

  • 格雷格,å,ä和ö的问题在于它们与ISO 8859-1重叠,所以它们让我感到紧张.我授予你"\ xE5\xE4\xF6"作为三个字节无效的UTF-8,但它只是一般的不安. (2认同)