Регулярные выражения: использование негативного взгляда вперед для неподдерживаемого негативного взгляда сзади и захват взгляда позади символов при разделении - PullRequest
3 голосов
/ 13 апреля 2019

Я снова борюсь с регулярными выражениями. Я пытался добавить использование escape-символа для экранирования пользовательского тега, такого как <1> до <57> и </1> до </57>. С помощью Georg, здесь , следующее выражение дает желаемый результат перед попыткой метода escape.

('This is a <21>test</21> again.').split(/(<\/?(?:[1-9]|[1-4][0-9]|5[0-7])>)/);

генерирует 'This is a ', '<21>', 'test', '</21>', ' again.'

В этом вопросе есть одно предположение об использовании негативного взгляда вперед и ИЛИ для аппроксимации неподдерживаемого негативного взгляда позади. Я изменил этот пример для того, что я считал своей более простой проблемой; однако я снова в тупике.

('This is a <21>test</21> again.').split(/(?:(?!\\).|^)(<\/?(?:[1-9]|[1-4][0-9]|5[0-7])>)/) );

создает 'This is a', '<21>', 'tes', '</21>', ' again.' Таким образом, он не включает в себя символ, предшествующий <21> или </21>, если не \. И я понимаю, почему с тех пор ?: использовал для не захвата.

Однако, если он удален, то:

('This is a <21>test</21> again.').split(/((?!\\).|^)(<\/?(?:[1-9]|[1-4][0-9]|5[0-7])>)/) );

генерирует 'This is a', ' ', '<21>', 'tes', 't', '</21>', ' again.' А предыдущий символ генерирует отдельный сплит.

Помимо этой проблемы, экранирование работает так, что когда предыдущий символ является \, тег не генерирует разбиение строки.

Не могли бы вы дать мне знать, если есть способ перехватить предыдущий символ, но включить его в текст предыдущей строки, а не в его собственный разделитель? И возможно исключить это только тогда, когда \?

Когда строка 'This is a <21>test</21> again.', желаемый результат 'This is a ', '<21>', 'test', '</21>', ' again.'

И когда это 'This is a \<21>test</21> again.', желаемый результат 'This is a <21>', 'test', '</21>', ' again.'

Спасибо.

Добавление После недавнего изучения использования встроенной функции в качестве параметра в операции replace с использованием регулярного выражения в этом документе MDN я начал задумываться о том, можно ли сделать что-то подобное здесь. Я ничего не знаю об измерении производительности, но сложность регулярного выражения, представленного Рево ниже, и его ответ на мой комментарий об эффективности, утверждающий, что отрицательный взгляд на будущее будет значительным улучшением эффективности и меньшим объемом работы для движка RegExp, и Кроме того, что RegExp является для меня чем-то вроде тайного черного ящика, побудившего меня экспериментировать с другим подходом. Это еще пара строк кода, но он дает тот же результат и использует гораздо более короткое регулярное выражение. Все, что он на самом деле делает, - сопоставляет теги как с экранирующим символом, так и без него, вместо того, чтобы пытаться исключить экранированные с помощью \, а затем игнорирует теги с экранирующим символом при построении массива. Фрагмент ниже.

Я не знаю, указывают ли времена, указанные в журнале консоли, на производительность, но, если это так, в примерах, которые я запускал, оказалось, что разница во времени между журналированием start и a.split значительно в процентах больше, чем в промежутке между a.split и окончательной регистрацией массива a при подходе exec.

Кроме того, самый внутренний блок if в операторе while предназначен для предотвращения сохранения "" в массиве, когда тег находится в начале или конце строки, или когда нет пробела. между двумя тегами.

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

let a, i = 0, l, p, r,
    x = /\\?<\/?(?:[1-9]|[1-4]\d|5[0-7])>/g,
    T = '<1>This is a <21>test<21> of \\<22>escaped and \\> </ unescaped tags.<5>';

console.log('start');

a = T.split(/((?:[^<\\]+|\\+.?|<(?!\/?(?:[1-9]|[1-4]\d|5[0-7])>))+|<\/?(?:[1-9]|[1-4]\d|5[0-7])>)/).filter(Boolean);

      console.log(a);
      a=[];
      while ( ( r = x.exec( T ) ) !== null) {
        if ( r[0].charAt(0) !== '\\' )
          {
             if ( r.index === 0 || r.index === p )
               {
                 a[ i ] = r[0];
                 i = i + 1;
               }
             else 
               {
                 a[ i ] = T.substring( p, r.index );
                 a[ i + 1 ] = r[0];
                 i = i + 2;
               }; // end if
             p = x.lastIndex;
          }; // end if
      }; // next while

      if ( p !== T.length ) a[i] = T.substring( p );
      console.log(a)

1 Ответ

2 голосов
/ 13 апреля 2019

Вы разбиваете на нужные подстроки и используете группу захвата для их вывода. Это может произойти и с нежелательными подстроками. Вы сопоставляете их и заключаете их в группу захвата, чтобы иметь их в выходных данных. Регулярное выражение будет:

(undesired-part|desired-part)

Регулярное выражение для нежелательных подстрок должно стоять на первом месте, потому что в них могут быть найдены нужные, т. Е. <21> включено в \<21>, поэтому мы должны соответствовать последним ранее.

Вы написали нужную часть, и она нам известна:

(undesired-part|<\/?(?:[1-9]|[1-4]\d|5[0-7])>)

Так что насчет нежелательного? Вот оно:

(?:[^<\\]+|\\.?|<(?!\/?(?:[1-9]|[1-4]\d|5[0-7])>))+

Давайте разберемся:

  • (?: Начало группы без захвата
    • [^<\\]+ Совпадает с чем угодно, кроме < и \
    • | или
    • \\.? Соответствует экранированному символу
    • | или
    • <(?!\/?(?:[1-9]|[1-4]\d|5[0-7])>) Соответствует <, что нежелательно
  • )+ Конец NCG, повторите как можно больше и хотя бы один раз

В целом это:

((?:[^<\\]+|\\.?|<(?!\/?(?:[1-9]|[1-4]\d|5[0-7])>))+|<\/?(?:[1-9]|[1-4]\d|5[0-7])>)

Js код:

console.log(
  'This is a \\<21>test</21> ag<ain\\.'.split(/((?:[^<\\]+|\\.?|<(?!\/?(?:[1-9]|[1-4]\d|5[0-7])>))+|<\/?(?:[1-9]|[1-4]\d|5[0-7])>)/).filter(Boolean)
);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...