Разделите строку, где разделители могут быть экранированы

avatar
Hexodus
7 апреля 2018 в 23:07
286
3
0

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

// Contents of str is exactly '|1|2|\|Three and Four\||5'
str.match(/[^|]/);

// Looking for: ['1', '2', '|Three and Four|', '5']

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

Я знаю, что это будет помечено как дубликат миллиарда других вопросов с регулярными выражениями, но я попытался применить другие решения здесь к своим собственным и поэкспериментировал с regex101.com. Увы, мой Regex Fu не силен.

P.s. Кто-нибудь знает какие-нибудь хорошие ресурсы для изучения регулярных выражений со вкусом JS?

Источник
user2864740
7 апреля 2018 в 23:17
0

@charlietfl | в этом столбце экранированы (\|) и, таким образом, не разделители. Рассмотрим вариант (где экранированные разделители не прилегают к обычным разделителям): |1|hello\|happy\|world|2 -> '1', 'hello|happy|world', '2'.

xianshenglu
7 апреля 2018 в 23:19
1

var str = '|1|2|\|Three and Four\||5';равно var str = '|1|2||Three and Four||5'; в js

Hexodus
7 апреля 2018 в 23:25
0

@xianshenglu @user2864740 Верно, не подумал прояснить это. Это поток данных, который я преобразовал в строку, чтобы иметь возможность манипулировать им и получать доступ к каждому элементу между |, но некоторые из элементов включают |, которые не следует рассматривать как разделители.

user2864740
7 апреля 2018 в 23:46
1

Возможный дубликат RegEx, необходимый для разделения строки javascript на "|" а не "\|" (там нашел кое-что :D)

user2864740
7 апреля 2018 в 23:52
0

Примечание: дубликат по-прежнему остается (фактически запрашивает такой) в \| -- это можно исправить, заменив \| в результирующих компонентах после расщепления.

Ответы (3)

avatar
user2864740
7 апреля 2018 в 23:30
1

При использовании JavaScript с обработчиком регулярных выражений, поддерживающим отрицательную обратную связь (например, Chrome), и в случае отображения только одного/простого перехода и отсутствия способа выхода из- escape, можно использовать относительно простую отрицательную обратную связь:

'|1|2|\\|Three and Four\\||5'.split(/(?<!\\)\|/)

# -> ["", "1", "2", "\|Three and Four\|", "5"]

Это говорит о том, что в Chrome, который поддерживает отрицательный просмотр назад, нужно разделить на "|" которому не предшествует "\".

Вот метод преобразования просмотра назад в просмотр вперед для совместимости с двигателем. Варианты также обсуждаются в RegEx, необходимом для разделения строки javascript на "|" но не "\|".

Однако, как указано выше, не касается символа \| последовательность и, таким образом, оставляет управляющую последовательность.


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

  1. Замените экранированные разделители "альтернативным" символом/строкой
  2. Разделить на оставшиеся (неэкранированные) разделители
  3. Преобразование «альтернативного» символа/строки обратно в отдельные компоненты

В коде

str = '|1|2|\\|Three and Four\\||5'

# replace \| -> "alternative"
# this assumes that \\| (escape-the-escape) is not allowed
rep = str.replace(/\\[|]/g, '~~~~')

# replace back, without any of the escapes
res = rep.split('|').map(function (f) { return f.replace(/~~~~/g, "|") })

# res -> ["", "1", "2", "|Three and Four|", "5"]
Hexodus
7 апреля 2018 в 23:47
1

Спасибо за это. Я думаю, что могу немного запутаться в символах \, я новичок в JS. Таким образом, окончательная строка должна содержать ровно |Three and Four| без косых черт.

user2864740
7 апреля 2018 в 23:49
1

@ Джейми4840 Ааа, да. Это потребует подкраски с разделением использования, показанным как исходное \| последовательность разделителей просто игнорируется.

Hexodus
7 апреля 2018 в 23:54
0

Ах, попался! Понял.

etuardu
7 октября 2021 в 14:08
0

Альтернативное решение по-прежнему требует метода экранирования для нового разделителя (~~~~), так что оно фактически переносит проблему на новый уровень, а не решает ее.

avatar
etuardu
7 октября 2021 в 15:47
0

Ответ Пола Г. Михая работает нормально, но не захватывает пустые строки: a||b|c вернет [ "a", "b", "c" ] вместо [ "a", "", "b", "c" ], как хотелось бы.

Исходя из его решения, можно также получить пустые строки, имитируя поведение split():

.
str.match(
  /((\\\|)|[^\|])*/gi
).filter(
  (e, i, a) => !(i > 0 && e == "" && a[i-1] != "")
)

Здесь я использую match() с тем же шаблоном, но допускаю совпадения нулевой длины (* вместо +).

Это дает мне массив совпадений с пустым строковым элементом для каждого найденного разделителя и в конце строки, например: a|b|c возвращает [ "a", "", "b", "", "c", "" ].

Затем я filter() отбрасываю все элементы пустой строки, которые идут после непустого элемента строки, поэтому я избавляюсь от ненужных элементов.

Похоже, это также корректно обрабатывает пограничные случаи:

a||b|c         → ["a", "", "b", "c"]
a|b|||c        → ["a", "b", "", "", "c"]
a|b\|b|c|      → ["a", "b\|b", "c", ""]
|a|\|b\||c|    → ["", "a", "\|b\|", "c", ""]
(empty string) → [""]
avatar
Paul G Mihai
7 апреля 2018 в 23:43
2

Это должно сделать это:

var str =  '|1|2|\\|Three and Four\\||5';
str.match(/((\\\|)|[^|])+/gi)

мой вывод таков:

 ["1", "2", "\|Three and Four\|", "5"]

Что я сделал, так это создал шаблон, соответствующий строке \| в первом подшаблоне, а затем сопоставил все, что не является |. Я также избежал \, потому что в противном случае запись этой строки в javascript просто проанализировала бы их на экранированный символ.