Опираясь на ваш шаблон, я удалил ненужные группы захвата и использовал простой -
, что эквивалентно (?:[-]{1})
. Вот что я придумала:
^(?:(?:^|;)(?=[^;]{1,15})[a-zA-Z0-9]+-[a-zA-Z0-9]+){1,20}$
Объяснение:
^
- совпадение начала строки
(?:...)
- группа без захвата
^|;
- чередование: совпадение ;
буквально или совпадение начала строки
[^;]{1,15};
- совпадение от 1 до 15 символов, отличных от ;
{1,20}
- соответствует предыдущему шаблону от 1 до 20 раз
$
- соответствует концу строки
Демо
РЕДАКТИРОВАТЬ: Шаблон:
^(?=[^;]{1,15})[a-zA-Z0-9]+-[a-zA-Z0-9]+(?:;(?=[^;]{1,15})[a-zA-Z0-9]+-[a-zA-Z0-9]+){0,19}$
не примет ;
в начале.
ВТОРОЕ РЕДАКТИРОВАНИЕ:
^(?=[^;]{1,15}(?:;|$))[a-zA-Z0-9]+-[a-zA-Z0-9]+(?:;(?=[^;]{1,15})[a-zA-Z0-9]+-[a-zA-Z0-9]+){0,19}$
Добавлено: (?:;|$)
- соответствует буквально ;
или $
- конец строки
Что он делает: правильно ограничивает длину токена до 15