Справка по регулярным выражениям: список, разделенный запятыми

#php #regex

#php #регулярное выражение

Вопрос:

Я пытаюсь (на php)

  1. Убедитесь, что содержимое текстовой области допустимо
  2. Разделите их на токены для обработки

Для того, чтобы считаться допустимым, это должна быть строка, содержащая только цифры и буквы, которые образуют «код» длиной от 3-6, и запятую, используемую для их разделения. Я разбил это на что-то вроде этого:

 [A-Za-z0-9]{3,6},
  

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

Например, строка ввода:

abe 123, PlE43,54drt, r2344

Должен быть разделен на следующие токены:

‘abe 123’ и ‘PlE43′ и ’54drt’ и ‘r2344’

Как я могу исправить мое регулярное выражение, чтобы оно соответствовало условиям? (прямо сейчас у меня возникли проблемы с тем, чтобы сделать запятую необязательной, но если она там есть, я ожидаю другого допустимого токена, а также использования запятой и любой комбинации пробелов до или после в качестве допустимого разделителя)

Комментарии:

1. Это один токен? abe 123

2. @Jon да, этот «код» является одним токеном

3. таким образом, токен скорее следует определять как [A-Za-z0-9 ]{3,6} . Обратите внимание на пробел.

4. @KarlsFriend: Я считаю, что это неправильно, потому что в этом случае abe 123 недопустимый токен (7 символов, если считать пробел). Смягчение грамматики для разрешения пробелов делает это довольно сложным.

5. 'abe 123' Токен должен быть недопустимым, поскольку он состоит из семи символов. Да?

Ответ №1:

Предполагая, что вы хотите свернуть все пробелы (т. Е. Следует игнорировать как пробелы внутри токена, так и пробелы между токенами / рядом с запятыми), вы можете сделать это намного проще с некоторой предварительной обработкой.

 $input = 'abe 123, PlE43,54drt , r2344';
$input = str_replace(' ', '', $input); // strip all spaces
$tokens = explode(',', $input);
foreach ($tokens as $token) {
    if(!preg_match('/^[A-Za-z0-9]{3,6}$/', $token)) {
        // error
    }
}
  

Этот код также выдаст сообщение об ошибке, если у вас есть две последовательные запятые или если вы заканчиваете входную строку запятой, потому что это сгенерировало бы пустой элемент в $tokens , который не проверяется по алфавитно-цифровому правилу 3-6.

Посмотрите это в действии.

Обновление: для сохранения пробелов внутри токенов потребуется небольшая модификация:

 $input = 'abe 123, PlE43,54drt , r2344';
$tokens = explode(',', $input);
foreach ($tokens as amp;$token) {
    $token = trim($token);
    if(!preg_match('/^[A-Za-z0-9]{3,6}$/', str_replace(' ', '', $token))) {
        // error
    }
}
  

Однако будьте осторожны с этим, поскольку он считает, что

  a         b                       42
  

является одним допустимым токеном.

Комментарии:

1. Обратите внимание, что это также уберет пробел из abe 123 в вашем примере. Запрашивающий может захотеть сохранить это.

2. @Justin Morgan Я бы хотел сохранить это пространство. Несмотря на то, что @Jon упоминает это в своем ответе, есть ли способ изменить его, чтобы сохранить это пространство?

3. @Diego: Смотрите обновление, но также имейте в виду, что правило 3-6 может странно взаимодействовать с правилом «пробелы допустимы».

4. @Diego — Вы могли бы сделать это так, как он делает здесь, или вы могли бы использовать regex для всего этого, как в моем собственном ответе. Это зависит от вас. Лично мне для этого нравится однострочное регулярное выражение, но это личные предпочтения. Он прав насчет правила 3/6, я не знаю, хотите ли вы включать пробелы в число или нет.

5. @JustinMorgan: Возможно, я бы тоже предпочел регулярное выражение, но вы пробовали проверять 123 abc (всего 7 символов) регулярным выражением? Он должен совпадать. Было бы трудно или невозможно сделать это правильно с помощью регулярного выражения.

Ответ №2:

Предполагаемые требования:

  • Каждый токен состоит из букв, цифр и пробелов, но должен содержать хотя бы одну букву и хотя бы одно число.
  • Общая длина токена составляет от 3-6 символов, включая любые внутренние пробелы.
  • Предполагается, что ограничение длины 3-6 символов включает любые пробелы. (Таким образом: «abe 123», который состоит из 7 символов, будет недействительным.)
  • Учитывая, что запятые, разделяющие токены, могут содержать необязательный пробел (который следует игнорировать), подразумевается, что токены никогда не могут начинаться или заканчиваться пробелом (но могут содержать встроенные пробелы).

Вот протестированная PHP-функция, которая проверяет заданную строку и возвращает массив, содержащий допустимые токены. Если строка недопустима, она возвращает false .

 // Return array of valid tokens else false if $text is invalid.
function valid_tokens($text) {
    $re_validate = '/
        # Validate comma separated TEXTAREA "Codes" tokens.
        ^                          # Anchor to start of string.
        s*                        # Optional leading whitespace.
        (?:                        # Group comma separated tokens.
          (?=[0-9 ]{0,5}[A-Za-z])  # Must contain at least one letter.
          (?=[A-Za-z ]{0,5}[0-9])  # Must contain at least one digit.
          [A-Za-z0-9]              # First char is number or digit.
          [A-Za-z0-9 ]{1,4}        # Middle chars numbers, digits or spaces.
          [A-Za-z0-9]              # Last char is number or digit.
          s*                      # Optional whitespace following token.
          (?:                      # Group for "end of token" options.
            ,s*                   # Either a comma, optional whitespace,
          | $                      # or end of string.
          )                        # End "end of token" options group.
        )                          # One or more tokens required.
        $                          # Anchor to end of string.
        /x';
    // Check validity of comma separated tokens (tokens may contain spaces).
    if (preg_match($re_validate, $text)) {
        $re_match = '/
            # Match next comma separated token. Capture in group $1.
            s*                  # Discard optional leading whitespace.
            (                    # $1: Comma separated token.
              [A-Za-z0-9]        # First char is number or digit.
              [A-Za-z0-9 ]{1,4}  # Middle chars numbers, digits or spaces.
              [A-Za-z0-9]        # Last char is number or digit.
            )                    # End $1: Comma separated token.
            s* ,?               # Discard comma separator if its there.
            /x';
        preg_match_all($re_match, $text, $matches);
        return $matches[1]; // Return array of valid tokens.
    }
    // Case 2: TEXTAREA does not contain valid tokens. Return false.
    else return FALSE;
}
  

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

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

Ответ №3:

попробуйте это:

 [A-Za-z0-9]{3,6}((s*,s*)[A-Za-z0-9]{3,6}|(s)*)
  

Ответ №4:

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

 ^([A-Za-z0-9]{3,6} *, *)*[A-Za-z0-9]{3,6}$
  

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

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

Ответ №5:

Я бы использовал следующее:

 /^(?:([A-Za-z0-9s]{3,6})s*(?:,s*|$))*/
  

Это поместит содержимое каждой текстовой области в группу захвата 1. Обратите внимание, что я изменил ваш [A-Za-z0-9] на [A-Za-z0-9s] , что позволит использовать пробелы в вашем abe 123 примере. Это также приведет к удалению пробелов вокруг запятой.

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

 /^(?:s*([A-Za-z0-9s]{3,6})s*(?:,|$))*/
  

Еще одно обновление: если вы хотите игнорировать пробелы в количестве 3-6 символов, вы можете сделать это:

 /^(?:((?:s*[A-Za-z0-9]s*){3,6})(?:,|$))*/