Как я могу изменить свое регулярное выражение на чтение UTF-8? - PullRequest
4 голосов
/ 08 февраля 2011

Я очень далеко продвинулся в сценарии, над которым я работаю, только чтобы выяснить, есть ли у него проблемы с чтением UTF-8 символов.

У меня есть контакт в Швеции, который сделал VM на своем компьютере с некоторым 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";
}

Я сузил его до своего регулярного выражения в качестве проблемы, потому что здесь необработанный вывод с сервера до применения регулярного выражения.

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

И это то, что я получаю после применения своего регулярного выражения

416
TEST

По какой-то причине регулярное выражение не соответствует, я просто не знаю почему. И текущее регулярное выражение в примере является третьей попыткой заставить его работать.

Полная строка, которую я сопоставляю, выглядит следующим образом. Мое регулярное выражение было сделано потому, что мне нужны только первые два блока информации, то есть выражение, которое вы хотите скопировать всю строку.

код:

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

Ответы [ 4 ]

7 голосов
/ 08 февраля 2011

Подшаблон

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

в вашем регулярном выражении означает «сопоставьте и запомните один или несколько символов, не являющихся символом новой строки, но остановитесь, как только вы найдете пробел», поэтому $name содержит TEST, поскольку шаблон прекратил совпадение, когда увидел пространство незадолго до этого. Box.

Вики VI Toolkit дает пример вывода подкоманды 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

Нежадный квантификатор +? означает совпадение одного или нескольких объектов, но совпадение хочет передать управление остальной части шаблона как можно быстрее. Помните, что [ имеет особое значение в регулярных выражениях, но шаблон \[ соответствует литералу, а не представляет класс символов.

Я думаю об этой технике, как обманывающий или растягивающий. Если вы хотите извлечь фрагмент текста, который трудно охарактеризовать, ищите окружающие функции, которые легко сопоставить - часто так просто, как ^ или $. Затем используйте растягивающийся узор, чтобы захватить все промежуточное, обычно (.+) или (.+?). Прочитайте раздел «Квантификаторы» документации perlre для объяснения множества вариантов.

Это решает непосредственную проблему, и вы также можете добавить полировку в нескольких областях.

Не используйте $1, $2 и друзей безоговорочно! Всегда проверяйте соответствие шаблона перед использованием переменных захвата. Например

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

Незащищенный print $1 может привести к неожиданным результатам, которые трудно отладить.

Разумное использование значений по умолчанию в Perl может помочь подчеркнуть вычисления и позволить механизму отойти на задний план. Отбрасывание $vm в пользу $_ в качестве переменной неявного цикла и цели неявного соответствия приводит к более хорошему результату.

Ваши комментарии просто переводят с Perl на английский. Самые полезные комментарии объясняют почему , а не что. Также имейте в виду совет Роба Пайка о комментировании :

Если вашему коду нужен комментарий для понимания, было бы лучше переписать его, чтобы его было легче понять.

В заданиях от %+ кавычки не делают ничего полезного. Значения уже являются строками, поэтому удалите кавычки.

my $id   = $+{id};
my $name = $+{name};

Ниже приведена модифицированная версия вашего кода, которая фиксирует все после числа, но до [store] в $name. utf8 pragma объявляет, что ваш исходный код - не так, как с обычной ошибкой - ваш ввод - содержит UTF-8. Приведенный ниже тест имитирует с помощью echo вывода из vim-cmd на шведской ВМ.

Как предположил Том, я использую модуль Encode для декодирования вывода, который поступает через соединение SSH, и кодирования его в интересах локального хоста перед его распечаткой.

Документация perlunifaq рекомендует декодировать внешние данные во внутренний формат Perl и затем кодировать любой вывод непосредственно перед его записью. Я предполагаю, что значение, возвращаемое из $ssh->capture(...), использует кодировку UTF-8, то есть удаленный хост отправляет UTF-8. Мы видим ожидаемый результат, потому что я работаю с современным дистрибутивом Linux и возвращаюсь к нему по ssh, но в дикой природе вы можете иметь дело с какой-то другой кодировкой.

Вы можете обойтись без пропуска вызовов на 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
TEST Box åäö!"''*#

4 голосов
/ 08 февраля 2011

Если вы знаете, что строка, над которой вы работаете, это UTF-8, а Net :: OpenSSH - нет (и, следовательно, не помечает ее как таковую), вы можете преобразовать ее во внутреннее представление, с которым Perl может работать с одним из :

use Encode;
decode_utf8( $in_place );
$decoded = decode_utf8( $raw );
3 голосов
/ 29 марта 2011

Последние версии Net :: OpenSSH имеют встроенную поддержку кодирования / декодирования кодировки в методах захвата:

my @getallvms = $ssh->capture({stream_encoding => 'utf8'},
                              'vim-cmd vmsvc/getallvms');
3 голосов
/ 08 февраля 2011

Итак, вы должны убедиться, что Perl понимает эти имена как строки в кодировке UTF-8. Пока я не думаю, что это так. Полный обзор о UTF-8 в Perl .

Вы можете проверить уникальность строк с помощью Encode::is_utf8 и декодировать их с помощью Encode::decode('UTF-8', $your_string).

UTF-8 довольно грязный в Perl, ИМХО. Вы должны быть довольно терпеливы с этим.

Чтобы печатать строки UTF-8 красивым способом, вы должны использовать что-то подобное в вашем скрипте:

BEGIN {
   binmode(STDOUT, ':encoding(UTF-8)');
   binmode(STDERR, ':encoding(UTF-8)');  # Error messages
}

Если вы понимаете, что Perl понимает ваши имена в UTF-8, вы также можете правильно их откорректировать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...