Частичное совпадение строки с регулярным выражением

Предположим, что у меня есть это регулярное выражение: /abcd/Предположим, что я хочу проверить ввод пользователя в отношении этого регулярного выражения и запретить ввод недопустимых символов на входе. Когда пользователь вводит "ab", он не подходит для соответствия регулярному выражению, но я не могу запретить вводить "a", а затем "b", так как пользователь не может вводить сразу все четыре символа (кроме копирования/вставки). Так что мне нужно здесь, это частичное совпадение, которое проверяет, может ли неполная строка потенциально соответствовать регулярному выражению.

Java имеет что-то для этой цели: .hitEnd() (описано здесь http://glaforge.appspot.com/article/incomplete-string-regex-matching). Python не делает это изначально, но имеет этот пакет, который выполняет эту работу: https://pypi.python.org/pypi/regex.

Я не нашел в нем никакого решения в js. Это было задано несколько лет назад: частичное совпадение Javascript RegEx и еще до этого: проверьте, является ли строка префиксом Javascript RegExp

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

Ответ 1

Похоже, вам повезло, я уже реализовал этот материал в JS (который работает для большинства моделей - возможно, этого вам будет достаточно). См. Мой ответ здесь. Вы также найдете там рабочую демоверсию.

Здесь нет необходимости дублировать полный код, я просто укажу на общий процесс:

  • Разберите входное регулярное выражение и выполните некоторые замены. Нет необходимости в обработке ошибок, так как вы не можете иметь недопустимый шаблон в объекте RegExp в JS.
  • Замените abc (?:a|$)(?:b|$)(?:c|$)
  • Сделайте то же самое для любых "атомов". Например, группа символов [ac] станет (?:[ac]|$)
  • Держите якоря как есть
  • Держите отрицательные

Если бы JavaScript имел более сложные функции регулярного выражения, это преобразование могло быть невозможным. Но с его ограниченным набором функций он может обрабатывать большинство входных регулярных выражений. Это приведет к неправильным результатам в регулярном выражении с обратными ссылками, хотя если ваша строка ввода заканчивается в середине соответствия ^(\w+)\s+\1$ (например, сопоставление ^(\w+)\s+\1$ от hello hel).

Ответ 2

Я думаю, что у вас должно быть 2 регулярных выражения для ввода /a?b?c?d?/ И один для тестирования в конце при вставке или выходе из ввода /abcd/

Это проверит действительный номер телефона:

const input = document.getElementById('input')

let oldVal = ''
input.addEventListener('keyup', e => {
  if (/^\d{0,3}-?\d{0,3}-?\d{0,3}$/.test(e.target.value)){
    oldVal = e.target.value
  } else {
    e.target.value = oldVal
  }
})
input.addEventListener('blur', e => {
  console.log(/^\d{3}-?\d{3}-?\d{3}-?$/.test(e.target.value) ? 'valid' : 'not valid')
})
<input id="input">

Ответ 3

Я сильно подозреваю (хотя я не уверен на 100%), что общий случай этой проблемы не имеет решения так же, как знаменитая проблема Тьюринга "Халтин" (см. " Неразрешимая проблема"). И даже если есть решение, скорее всего, это будет не то, что действительно хотят пользователи, и, следовательно, в зависимости от вашей строгости приведет к плохому UX.

Пример:

Предположим, что "target RegEx" - это [a,b]*c[a,b]* также предположим, что вы создали разумный на первый взгляд "тест RegEx" [a,b]*c?[a,b]* (очевидно, два c в строке неверно, да?) и предположим, что текущий пользовательский ввод - aabcbb но есть опечатка, потому что то, что действительно хотел пользователь, - aacbbb. Существует много способов устранить эту опечатку:

  • удалить c и добавить его до первого b - будет работать нормально
  • удалить первый b и добавить после c - будет работать нормально
  • добавьте c перед первым b а затем удалите старый. Ой, мы запрещаем этот ввод как недействительный, и пользователь сойдет с ума, потому что нормальный человек не может понять такую логику.

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

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

Так что делать? Я думаю, что единственное, что вы можете сделать и по-прежнему получать разумный UX - это самое простое, что вы можете сделать, просто проанализируйте свой "целевой регекс" для набора допустимых символов и сделайте свой "тест RegEx" [set of allowed chars]*. И да, если содержит "целевой регекс" . wildcart, вы не сможете делать какую-либо разумную фильтрацию вообще.

Ответ 4

Это трудное решение для тех, кто считает, что нет никакого решения: реализовать версию python (https://bitbucket.org/mrabarnett/mrab-regex/src/4600a157989dc1671e4415ebe57aac53cfda2d8a/regex_3/regex/_regex.c?at=default&fileviewer= file-view-default) в js. Так что можно. Если у кого-то есть более простой ответ, он выиграет щедрость.

Пример использования модуля python (регулярное выражение с обратной ссылкой):

$ pip install regex
$ python
>>> import regex
>>> regex.Regex(r'^(\w+)\s+\1$').fullmatch('abcd ab',partial=True)
<regex.Match object; span=(0, 7), match='abcd ab', partial=True>

Ответ 5

Ребята, вы, вероятно, найдете эту страницу интересной:

(https://github.com/desertnet/pcre)

Это была смелая попытка: создать реализацию WebAssembly, которая будет поддерживать PCRE. Я все еще играю с этим, но я подозреваю, что это не практично. Двоичный файл WebAssembly весит ~ 300K; и если ваш JS неожиданно завершает работу, вы можете не уничтожить модуль и, следовательно, потерять значительную память.

Суть в том, что это, очевидно, то, что люди ECMAscript должны формализовать, и производители браузеров должны предоставить (благодарность разработчику WebAssembly, возможно, позоря их за то, что они попали на карту...)

Я недавно пытался использовать атрибут "pattern" элемента input [type = 'text']. Я, как и многие другие, нашел, что это разочарование, которое не будет подтверждено, пока форма не будет отправлена. Таким образом, человек будет тратить свое время, набирая (или вставляя...) многочисленные символы и перепрыгивая на другие поля, только чтобы узнать после отправки формы, что они ввели это поле неправильно. В идеале я хотел, чтобы он сразу же проверял ввод данных в поле, поскольку пользователь вводит каждую клавишу (или во время вставки...)

Хитрость в том, чтобы выполнить частичное совпадение с регулярным выражением (до тех пор, пока сотрудники ECMAscript и производители браузеров не получат его вместе с PCRE...), состоит не только в том, чтобы указать регулярное выражение в шаблоне, но и соответствующие значения шаблона в качестве атрибута данных. Если ваше поле ввода короче, чем шаблон (или input.maxLength...), он может использовать их в качестве суффикса для целей проверки. ДА -this не будет практичным для регулярных выражений со сложными случаями; но для сопоставления с шаблоном с фиксированной позицией -which обычно, что needed- хорошо (если вам нужно что-то более сложное, вы можете использовать методы, показанные в моем коде...)

Пример для адреса биткойн [Есть ли ваше внимание сейчас? -OK, а не люди, которые не верят в технологии цифровых валют...] Ключевая функция JS, которая делает это, - validatePattern. Элемент ввода в разметке HTML будет указан следующим образом:

<input id="forward_address"
       name="forward_address"
       type="text"
       maxlength="90"
       pattern="^(bc(0([ac-hj-np-z02-9]{39}|[ac-hj-np-z02-9]{59})|1[ac-hj-np-z02-9]{8,87})|[13][a-km-zA-HJ-NP-Z1-9]{25,34})$"
       data-entry-templates="['bc099999999999999999999999999999999999999999999999999999999999','bc1999999999999999999999999999999999999999999999999999999999999999999999999999999999999999','19999999999999999999999999999999999']"
       onkeydown="return validatePattern(event)"
       onpaste="return validatePattern(event)"
       required
/>

[Кредит переходит к этому сообщению: "RegEx соответствует адресам Биткойн? "Примечание для приверженцев старой школы биткойнов, которые будут осуждать использование нуля в регулярном выражении здесь -it, просто пример для выполнения ПРЕДВАРИТЕЛЬНОЙ проверки; сервер, принимающий адрес, переданный браузером, может выполнить вызов RPC после форма сообщения, чтобы проверить его более строго. Настройте свое регулярное выражение в соответствии с.]

Точный выбор символов в шаблоне ввода данных был немного произвольным; но они должны были быть такими, чтобы, если ввод, который вводит или вставляет пользователь, по-прежнему был неполным по длине, он использовал их как оптимистическую замену, и до сих пор ввод будет считаться действительным. В этом примере для последнего из шаблонов ввода данных ('19999999999999999999999999999999999') это было "1", за которым следуют 39 девяток (видя, что спецификация регулярного выражения "{25,39}" диктует, что максимум 39 цифр во втором символьном интервале/группе...) Поскольку ожидалось, что -the префикс "bc" будет иметь две формы и более старый "1"/"3" prefix- Я предоставил несколько шаблонов для средство проверки, которое нужно попробовать (если оно проходит только один из них, оно проверяет...) В каждом случае шаблона я предоставлял максимально длинный шаблон, чтобы обеспечить максимально допустимую возможность с точки зрения длины.

Если бы вы создавали эту разметку на сервере динамического веб-контента, пример с шаблонными переменными (a la django...) будет выглядеть так:

 <input id="forward_address"
        name="forward_address"
        type="text"
        maxlength="{{MAX_BTC_ADDRESS_LENGTH}}"
        pattern="{{BTC_ADDRESS_REGEX}}" {# base58... #}
        data-entry-templates="{{BTC_ADDRESS_TEMPLATES}}" {# base58... #}
        onkeydown="return validatePattern(event)"
        onpaste="return validatePattern(event)"
        required
/>

[Имейте в виду: я пошел в более глубокий конец бассейна здесь. С тем же успехом вы можете использовать это для более простых шаблонов проверки.]

И если вы предпочитаете не использовать атрибуты события, а прозрачно привязать функцию к событиям элемента при загрузке документа -knock самостоятельно.

Вы заметите, что нам нужно указать validatePattern для трех событий:

  • Клавиша для перехвата клавиш удаления и возврата.

  • Вставка (буфер вставляется в значение поля, и если он работает, он принимает его как действительный; если нет, вставка не выполняется...)

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

А вот ссылка на [код, свободный от зависимостей], который творит чудеса:

https://gitlab.com/osfda/validatepattern.js

(Если это вызовет интерес, я объединю конструктивные и практические предложения и предоставлю более подробную информацию...)

PS: пакет инкрементных регулярных выражений, размещенный выше Лукасом Тресневским:

  • Похоже, не были обновлены? (Я видел признаки того, что он подвергался модификации??)

  • Не подвергается браузерной проверке (попытался сделать это с ним, чтобы пнуть шины на нем -it был беспорядок в модуле; приглашаем кого-либо еще здесь, чтобы опубликовать версию с браузерной проверкой для тестирования. Если это сработает, я интегрирую это с моими хуками проверки ввода и предложите его в качестве альтернативного решения...) Если вам удастся получить его в браузере, возможно, если вы поделитесь точными шагами, которые были необходимы, это также наставит всех на этом посте. Я пытался использовать пакет esm для исправления несовместимости версий, с которыми сталкивался browserify, но ничего не вышло...