RegEx не работает для некоторых условий

1

Я должен проверять поля Name в моем проекте и иметь для него различные условия проверки.

Начальное название:

  1. Минимум длиной 2 символа.
  2. Могут быть алфавитные символы, пробелы и дефисы.
  3. Первый символ должен быть буквенным.
  4. Бланки и дефисы не должны быть смежными

Мое регулярное выражение:

function fname(value){
var fn = new RegExp("([a-zA-Z]{1}[a-zA-Z]*[ -]{0,1}[a-zA-Z])+([ -]{0,1}[a-zA-Z]+)*");
if(fn.test(value)){
          return true;  
         }
         else{          
            return false;
         }
}

Фамилия:

  1. Могут быть алфавитные символы, пробелы, дефисы и апострофы.
  2. 1-й символ должен быть буквенным.
  3. Бланки, дефисы и апострофы не должны быть смежными.
  4. 1 char допустим, только если этот символ равен O

Мое регулярное выражение:

function fname(value){
    var ln = new RegExp("([a-zA-Z]+([a-zA-Z]|([ '][a-zA-Z])|([-][a-zA-Z])){1,}|[O]{1})");
    if(ln.test(value)){
              return true;  
             }
             else{          
                return false;
             }
    }

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

  1. Буквенно-цифровые символы становятся приемлемыми, чего не должно быть.
  2. пространство, дефис соседний (в случае начального имени) и пробел, дефис и апострофы, смежные (в случае фамилии) в любом положении в строке.
  • 1
    {1} избыточен, а {0, 1} такой же, как ? ,
  • 1
    Также просто return ln.test(value);
Показать ещё 7 комментариев
Теги:

3 ответа

1
Лучший ответ

Хотя это можно сделать с помощью одного регулярного выражения, самым простым решением является просто отделить тесты:

function is_valid_first_name(str) {
    return (
        str.length >= 2 &&
        /^[a-zA-Z \-]*$/.test(str) &&
        /^[a-zA-Z]/.test(str) &&
        !/[ \-]{2}/.test(str)
    );
}

function is_valid_last_name(str) {
    return (
        /^[a-zA-Z \-']*$/.test(str) &&
        /^[a-zA-Z]/.test(str) &&
        !/[ \-']{2}/.test(str) &&
        (str.length > 1 || str === 'O')
    );
}
0

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

> "Hello".match(/[a-z]+/)
[ 'ello', index: 1, input: 'Hello' ]
> "Hello".match(/^[a-z]+$/)
null

Еще одна уродливая вещь, которую я вижу, - невидимые дефисы в классах персонажей ([ -]). Даже в этом случае действителен (он будет работать так, как вы ожидаете), но он уродлив, потому что он имеет особое значение в классах символов (в зависимости от контекста), поэтому минимальные и, по-видимому, безобидные изменения могут нарушить работу, которая уже работает:

> "-".match(/[abc-]/);
[ '-', index: 0, input: '-' ]
> "-".match(/[abc-j]/);
null

Отвечая на ваш вопрос: Игнорирование 4-го правила довольно легко:

> "hello-world as foo".match(/^[a-zA-Z][a-zA-Z\s\-]+$/)
[ 'hello-world as foo', index: 0, input: 'hello-world as foo' ]
  • Первый класс символов ([a-zA-Z]) обеспечивает второе правило и совместим с правилами n. 1 и 3 тоже.

  • Второй класс символов ([a-zA-Z\s\-]) соответствует любому действительному символу в соответствии с правилом n. 2.

  • Следующий квантификатор (+) обеспечивает одно или несколько вхождений, которые плюс начальный символ, соответствующий начальным символьным классам, составляет 2 или более символов (так соответствует правилу n. 1).

  • Наконец, начальная и конечная границы ^ и $ гарантируют, что сопоставление начинается с действительно начала строки и заканчивается на ее конце, поэтому, как я объяснял ранее, совпадающих подстрок недостаточно, чтобы проверить наличие недопустимых символов.

Четвертое правило немного сложнее. Я думаю, к нему могут подойти выражения lookbehind и lookahead, но они недоступны для всех механизмов регулярных выражений (даже в javascript, я думаю, что они не являются, по крайней мере, в некоторых древних версиях).

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

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

НО, если вы не будете вынуждены использовать одно регулярное выражение, довольно легко проверить раздельное выражение ad hoc.

function fname(value){
    return !!( // <- (optional) Boolean cast for more consistent return type.
        value.match(/^[a-zA-Z][a-zA-Z\s\-]+$/)
        && ! value.match(/\s-|-\s/)
    );
}

console.log (fname("hello-world as foo")); // true
console.log (fname("hello- world as foo")); // false
console.log (fname("hello -world as foo")); // false
console.log (fname("-hello-world as foo")); // false (null without "!!" cast).
console.log (fname(" hello-world as foo")); // false (null without "!!" cast).

... как заключительная заметка, я использовал класс символов " \s " вместо " "для пробелов. Это также соответствует другим символам интервала (например, вкладкам и, в некоторых случаях, разрывам строк и т.д.), если вы не хотите принимать эти символы, замените все" \s "на простые пробелы.

Я предпочитал использовать " \s " для удобства чтения (и потому, что в большинстве случаев мне это нравится гораздо больше, если другие расстояния приемлемы, но я думаю, что в этом случае это не так).

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

0

Во-первых, обе функции имеют одно и то же имя. Может быть, это просто опечатка.

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

const re_fname = /^[a-zA-Z](?:[- ]?|(?:[a-zA-Z][- ]?)+)$/;

function fname(value){
  const res = re_fname.test(value);
  console.log("%s: %s", res ? "PASS" : "FAIL", value);
  return res;
}

fname("foo-bar");
fname("foobar");
fname("f-");
fname("f--");
fname("foo-bar-");
fname("foo-bar--");
fname("-foo-bar");
fname("foo--bar");
fname("foo bar");
fname("foo  bar");
fname("foo- bar");

И фамилия, которая почти идентична, только добавляет апостроф к набору и допускает одно соответствие O

const re_lname = /^(?:O|(?:[' -]?|[a-zA-Z](?:[' -]?[a-zA-Z])+)[' -]?)$/;
  
function lname(value){
  const res = re_lname.test(value);
  console.log("%s: %s", res ? "PASS" : "FAIL", value);
  return res;
}

lname("O");
lname("X");
lname("foobar");
lname("foo-bar");
lname("foo-bar-");
lname("foo-bar-'");
lname("foo-bar'");
lname("-foo-bar");
lname("foo--bar");
lname("foo bar");
lname("foo  bar");
lname("foo- bar");
lname("foo'bar");
lname("foo' bar");
lname("foo'- bar");
lname("foo-'bar");
  • 0
    Почему foo-bar- терпит неудачу? Это не нарушает одно из правил.
  • 0
    @ibrahimmahrir: я отметил причину в своем ответе. «Это не допускает конечного пробела или дефиса, но это, кажется, подразумевается».
Показать ещё 5 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню