Лучший способ разобрать строку в Perl - PullRequest
0 голосов
/ 07 июня 2018

Чтобы выполнить задачу ниже, я написал ниже C как программу perl (поскольку я новичок в Perl), но я не уверен, является ли это лучшим способом для достижения.

Может кто-нибудь, пожалуйста, руководство?Примечание: Не с полной программой, но где я могу улучшить.

Заранее спасибо

Ввод:

$str = "mail1, local<mail1@mail.local>, mail2@mail.local, <mail3@mail.local>, mail4 local<mail4@mail.local>"

Ожидаемый вывод:

mail1, local<mail1@mail.local>
mail2@mail.local
<mail3@mail.local>
mail4, local<mail4@mail.local>

Пример программы

my $str="mail1, \@local<mail1\@mail.local>, mail2\@mail.local, <mail3\@mail.local>, mail4, local<mail4\@mail.local>";
my $count=0, @array, $flag=0, $tempStr="";
for my $c (split (//,$str)) {
    if( ($count eq 0) and ($c eq ' ') ) {
        next;
    }
    if($c) {
        if( ($c eq ',') and ($flag eq 1) ) {
            push @array, $tempStr;
            $count=0;
            $flag1=0;
            $tempStr="";
            next;
        }
        if( ($c eq '>' ) or ( $c eq '@' ) ) {
            $flag=1;
        }
        $tempStr="$tempStr$c";
        $count++;
    }
}
if($count>0) {
    push @array, $tempStr;
}
foreach my $var (@array) {
    print "$var\n";
}

Редактировать:

Ввод:

Input is the output of above code.

Ожидаемый вывод:

"mail1, local"<mail1@mail.local>
"mail4, local"<mail4@mail.local>

Пример кода:

$str =~ s/([^@>]+[@>][^,]+),\s*/$1\n/g;
my @addresses = split('\n',$str);
if(scalar @addresses) {
    foreach my $address (@addresses) {
        if (($address =~ /</) and ($address !~ /\"/) and ($address !~ /^</)){
            $address="\"$address";
            $address=~ s/</\"</g;
        }
    }
    $str = join(',',@addresses);
}
print "$str\n";

Ответы [ 2 ]

0 голосов
/ 07 июня 2018

Вот подход, использующий split , который в этом случае также нуждается в тщательном регулярном выражении

use warnings;
use strict;
use feature 'say';

my $string =   # broken into two parts for readabililty
    q(mail1, local<mail1@mail.local>, mail2@mail.local, )
 .  q(<mail3@mail.local>, mail4, local<mail4@mail.local>);

my @addresses = split /@.+?\K,\s*/, $string;

say for @addresses;

split принимает полное регулярное выражение в своей спецификации разделителя.В этом случае я полагаю, что каждая запись отделяется запятой, которая идет после адреса электронной почты, поэтому @.+?,

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

Вместо этого мы обычно можем сопоставить шаблон @.+?, а затем использовать \K форму (вид сзади), которая отбрасывает все предыдущие совпадения, чтобы они не были взяты из строки,Таким образом, вышеприведенный код разделяется на ,\s*, когда этому предшествует адрес электронной почты @... (что не используется).

Он печатает

mail1, local<mail1@mail.local>
mail2@mail.local
<mail3@mail.local>
mail4, local<mail4@mail.local>

Правкаспрашивает о цитировании описания, предшествующего <...>, когда оно там.Простой способ - сделать еще один проход, как только адреса были проанализированы из строки, как указано выше.Например,

my @addresses = split /@.+?\K,\s*/, $string;   #/ stop syntax highlight

s/(.+?,\s*.+?)</"$1"</  for @addresses;

say for @addresses;

Регулярное выражение в цикле - это один из способов изменить элементы массива.Я использую его для его эффективности (изменения элементов на месте), краткости и в качестве демонстрации следующих свойств.

В цикле foreach переменная индекса (или $_) является псевдонимом для обрабатываемого в данный момент элемента , поэтому при его изменении изменяется этот элемент.Это известный источник ошибок, когда допускается неосознанно, что было еще одной причиной показать его в приведенной выше форме.

В операторе также используется модификатор оператора , и он эквивалентен

foreach my $elem (@addresses) {
    $elem =~ s/(.+?,\s*.+?)</"$1"</;
}

Этот способ часто считается более правильным, но я обнаружил, что другая форма более четко подчеркивает, что элементы меняются, когда это является единственной целью foreach.

0 голосов
/ 07 июня 2018

Как я вижу, вы хотите заменить каждое:

  • запятой и следующими пробелами,
  • , встречающееся после @ или >,

с новой строкой.

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

Часть поиска может быть следующей:

([^@>]+[@>][^,]+),\s*

Подробности:

  • ( - Начало 1-й группы захвата.
    • [^@>]+ - непустая последовательность символов, отличная от @ или >.
    • [@>] - либо @, либо >.
    • [^,]+ - непустая последовательность символов, отличная от запятой.
  • ) - конец 1-й группы захвата.
  • ,\s* - Запятая и необязательная последовательность пробелов.

Заменяемая часть должна быть:

  • $1 - 1-я группа захвата.
  • \n - Новая строка.

Таким образом, вся программа, намного короче вашей, может выглядеть следующим образом:

my $str='mail1, local<mail1@mail.local>, mail2@mail.local, <mail3@mail.local>, mail4, local<mail4@mail.local>';
print "Before:\n$str\n";
$str =~ s/([^@>]+[@>][^,]+),\s*/$1\n/g;
print "After:\n$str\n";

Заменить все необходимые запятые Я использовал опцию g.

Обратите внимание, что я поместил исходную строку в одинарные кавычки, иначе Perl жаловался бы на Возможна непреднамеренная интерполяцияиз @ mail .

Редактировать

Ваши измененные требования должны обрабатываться по-другому.«Обычная» замена не возможна, потому что теперь есть некоторые фрагменты для match и некоторые фрагменты для ignore .

Таким образом, основная идея - написать while цикл с соответствующим регулярным выражением: (\w+),?\s+(\w+)(<[^>]+>), что означает:

  • (\w+) - Первая группа захвата - последовательность символов слова (например, mail1).
  • ,?\s+ - Необязательная запятая и последовательность пробелов.
  • (\w+) - Вторая группа захвата - последовательность символов слова (например, local).
  • (<[^>]+>) - Третья группа захвата - последовательность символов, отличных от > (фактический почтовый адрес), заключенная в угловые скобки, например, <mail1@mail.local>.

В каждом выполнении цикла у вас есть доступгруппам, захваченным в этом конкретном совпадении ($1, $2, ...).

Таким образом, содержимое этого цикла состоит в печати всех этих захваченных групп с необходимыми дополнительными символами.

Код (опять же намного короче вашего) должен выглядеть так:

my $str = 'mail1, local<mail1@mail.local>, mail2@mail.local, <mail3@mail.local>, mail4 local<mail4@mail.local>';
while ($str =~ /(\w+),?\s+(\w+)(<[^>]+>)/g) {
  print "\"$1, $2\"$3\n";
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...