我正在编写一个脚本,但发现它在阅读 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 的内部格式恰好与您正在使用的主机匹配。然而,一般来说,偷工减料会给你带来麻烦:最后,代码!
#! /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/