RegEx replace возвращает неожиданный результат без.*

Я пытаюсь создать регулярное выражение, которое выполняет следующие преобразования:

  1. Apple Orange > AO
  2. Load Module > LM
  3. anApple Orange > O
  4. toLoad Module > M

Я нашел подходящий образец, но заметил странное поведение. Вот моя первоначальная попытка:

/^([A-Z])?[^ ]* ([A-Z])/

Выполнение замены в третьем (и четвертом) тестовом примере с этим выражением дает мне удивительный результат:

'anApple Orange'.replace(/^([A-Z])?[^ ]* ([A-Z])/,'$1$2')
> "Orange"

Почему это удивительно? Ну, первая группа, очевидно, не совпадает, поскольку строка не начинается с заглавной буквы, а вторая группа выбирает только одну заглавную букву: ([A-Z]) не все после него ([A-Z].*)

К моему удивлению, добавив .* сразу после последнего захвата группа дала мне правильный результат:

'anApple Orange'.replace(/^([A-Z])?[^ ]* ([A-Z]).*/,'$1$2')
> "O"

Почему это происходит за пределами моего понимания JS и регулярных выражений. Я очень рад узнать, что за темная магия вызывает у нас [A-Z] вернуть несколько, и даже несколько строчных букв.

Вот работающая демонстрация:

var testCases = [
      'Apple Orange',
      'Load Module',
      'anApple Orange',
      'toLoad Module'
    ],
    badregex = /^([A-Z])?[^ ]* ([A-Z])/,
    goodregex = /^([A-Z])?[^ ]* ([A-Z]).*/;

document.onreadystatechange = function(n){
  if (document.readyState === "complete"){
      for (var i=0,l=testCases.length; i<l; i++){
        var p = document.createElement('p'),
            testCase = testCases[i];
        p.innerHTML = ""+testCase+" &gt; "+testCase.replace(badregex,'$1$2')
        document.body.appendChild(p);
      }
      document.body.appendChild(document.createElement('hr'));
      for (var i=0,l=testCases.length; i<l; i++){
        var p = document.createElement('p'),
            testCase = testCases[i];
        p.innerHTML = ""+testCase+" &gt; "+testCase.replace(goodregex,'$1$2')
        document.body.appendChild(p);
      }
  }
}

3 ответа

Решение

Я бы хотел,

> "Apple Orange".replace(/(?:^|\s)([A-Z])|./g, "$1")
'AO'

Не усложняй вещи. Просто запишите все заглавные буквы, которые существуют сразу после пробела или в начале. А затем сопоставьте все оставшиеся символы. Теперь замените все соответствующие символы на $1, Обратите внимание, что все совпадающие символы заменяются символами, присутствующими внутри запасной части.

DEMO

Зачем?

'anApple Orange'.replace(/^([A-Z])?[^ ]* ([A-Z])/,'$1$2')
> "Orange"
  • ([A-Z])? проверяет наличие дополнительной заглавной буквы в начале. Там нет такой вещи. Таким образом, он захватывает пустую строку.
  • [^ ]* соответствует нулю или более непробельных символов.
  • <space> соответствует пробелу
  • ([A-Z]) захватывает только первую букву оранжевого цвета.
  • Теперь, заменив все сопоставленные символы с $1 -> пустая строка $2 -> O дам тебе Orange

Ваш первый пример соответствует anApple O, $1 пусто, потому что ^([A-Z])? не является обязательным и не соответствует и $2 является O так ты заменишь anApple O от O в строке anApple Orange и это приведет к Orange

Вместо того, чтобы использовать replace со сложным регулярным выражением вы можете использовать очень простое регулярное выражение с match и использовать join чтобы получить желаемый результат:

'anApple Orange'.match(/\b([A-Z])/g).join('')
//=> O

'Apple Orange'.match(/\b([A-Z])/g).join('')
//=> AO
Другие вопросы по тегам