Есть ли регулярное регулярное разбиение строки, содержащей escape-последовательности? - PullRequest
2 голосов
/ 09 июля 2010

Учитывая строку значений, разделенных каналом (назовите это $psv), я хочу иметь возможность разделить по этим каналам и заполнить массив.Однако строка также может содержать экранированные каналы (\|) и экранированные экранированные символы (\\), которые следует рассматривать как простые литералы.У меня есть пара решений этой проблемы:

  • Замените обе escape-последовательности случайными строками, которых нет в $psv, split(/\|/, $psv), замените оригинальные символы
  • Цикл $psv, символ за символом

И я думаю, что оба из них будут работать.Но для максимального притока допамина я бы хотел сделать это с помощью одного split() вызова и ничего больше.Так есть ли регулярное выражение для этого?

Ответы [ 4 ]

4 голосов
/ 09 июля 2010

Есть ли конкретная причина, по которой вам требуется решение pure regex ?(если, конечно, этот вопрос не был скорее умственным вызовом и практической проблемой).

Надлежащим способом обработки данных, разделенных X, в реальном коде является использование правильного синтаксического анализатора - очень распространенногоText::CSV_XS (не позволяйте имени обмануть вас - он может обрабатывать любые символы-разделители, не только запятые).Он будет корректно обрабатывать экранирование, наряду с цитированием.

4 голосов
/ 09 июля 2010

Вам не нужно использовать split для этой задачи. Альтернатива:

my $psv = "aaa|bbb||ccc|\\|\\|\\||\\\\\\\\\\\\";
print "$psv\n";

my @words = map { s/\\([\\|])/$1/g; $_; } ($psv =~ /(?:^|\|) ((?:\\[\\|] | [^|])*)/gx);
printf("%s\n", join(", ", @words));

Регулярное выражение может выглядеть страшно, но его легко объяснить. Это соответствует каждому из слов, которые разделены трубами. Он начинается либо в начале строки, либо в разделителе канала. Затем следует произвольное число либо escape-последовательности (\ + один из \|), либо произвольного символа, кроме pipe.

Регулярное выражение внутри map просто заменяет escape-последовательности тем, что они действительно означают.

2 голосов
/ 09 июля 2010

Если Perl поддерживает утверждения с изменяемой шириной, вы можете сделать это примерно так:

split(/(?<!(?<!\\)(?:\\\\)*\\)\|/, $psv);

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

Еще кое-что, что, я полагаю, вы могли бы попробовать - просто разбить строку на символе канала, а затем проверить каждый элемент результирующего списка, чтобы увидеть, заканчивается ли он нечетным числом обратных слешей. Если это так, присоедините его к следующему элементу списка с | между ними. По сути, вы выполняете разделение, игнорируя escape-последовательности, затем возвращаетесь назад и учитываете escape-последовательности впоследствии.

0 голосов
/ 09 июля 2010

Более сладкий раствор

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


#!/usr/bin/perl -w

use strict;

   sub main{
      (my $psv = <DATA>) =~ s/\s+$//s;

      my @arr = $psv =~ /(?:^|\G\|)((?:[^\\|]|\\.)*)/sg;

      {
         local $" = ', ';      # $" - sets the pretty print
         print "@arr \n";      # outputs: abc, def, g\|i, jkl, m\|o, pqr, s\\u, v\w, x\\, , z 
      }

   }

   main();


__DATA__
abc|def|g\|i|jkl|m\|o|pqr|s\\u|v\w|x\\||z
...