Замените группу захвата Regex заглавными буквами в Javascript - PullRequest
67 голосов
/ 26 мая 2011

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

> a="foobar"
'foobar'
> a.replace( /(f)/, "$1".toUpperCase() )
'foobar'
> a.replace( /(f)/, String.prototype.toUpperCase.apply("$1") )
'foobar'

Не могли бы вы объяснить, что не так с этим кодом?

Ответы [ 5 ]

137 голосов
/ 26 мая 2011

Вы можете передать функцию в replace.

var r = a.replace(/(f)/, function(v) { return v.toUpperCase(); });

Объяснение

a.replace( /(f)/, "$1".toUpperCase())

В этом примере вы передаете строку в функцию замены.Поскольку вы используете специальный синтаксис замены ($ N захватывает N-й захват) , вы просто даете то же значение.toUpperCase фактически вводит в заблуждение, потому что вы делаете заглавную строку замены только 1013 * (что несколько бессмысленно, потому что символы $ и один 1 не имеют верхнего регистра, поэтому возвращаемое значение все равно будет "$1") .

a.replace( /(f)/, String.prototype.toUpperCase.apply("$1"))

Верьте или нет, семантика этого выражения точно такая же.

11 голосов
/ 08 мая 2014

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

a.replace('f', String.call.bind(a.toUpperCase));

Итак, где вы ошиблись и что это за новое вуду??

Задача 1

Как указано выше, вы пытались передать результаты вызванного метода в качестве второго параметра String.prototype.replace () , когдавместо этого вы должны передавать ссылку на функцию

Решение 1

Это достаточно просто решить.Простое удаление параметров и круглых скобок даст нам ссылку, а не выполнение функции.

a.replace('f', String.prototype.toUpperCase.apply)

Задача 2

Если вы попытаетесь запустить код сейчас, вы получите сообщение о том, что неопределенноене является функцией и поэтому не может быть вызвана.Это потому, что String.prototype.toUpperCase.apply на самом деле является ссылкой на Function.prototype.apply () через прототипическое наследование JavaScript.То, что мы на самом деле делаем, выглядит примерно так:

a.replace('f', Function.prototype.apply)

Что явно не то, что мы намеревались.Как узнать, что Function.prototype.apply () в String.prototype.toUpperCase () ?

Решение 2

Использование Function.prototype.bind () мы можем создать копию Function.prototype.call с его контекстом, специально установленным в String.prototype.toUpperCase.Теперь у нас есть следующая

a.replace('f', Function.prototype.apply.bind(String.prototype.toUpperCase))

Проблема 3

Последняя проблема заключается в том, что String.prototype.replace () будет передавать несколько аргументов своей функции замены.Однако Function.prototype.apply () ожидает, что вторым параметром будет массив, но вместо этого он получит либо строку, либо число (в зависимости от того, используете ли вы группы захвата или нет).Это приведет к ошибке списка недопустимых аргументов.

Решение 3

К счастью, мы можем просто заменить в Function.prototype.call () (которая принимает любое количество аргументов, ни один из которых не имеет ограничений типа) для Function.prototype.apply () .Теперь мы пришли к рабочему коду!

a.replace(/f/, Function.prototype.call.bind(String.prototype.toUpperCase))

Сброс байтов!

Никто не хочет набирать prototype несколько раз.Вместо этого мы будем использовать тот факт, что у нас есть объекты, которые ссылаются на одни и те же методы через наследование.Конструктор String, будучи функцией, наследует от прототипа Function.Это означает, что мы можем заменить в String.call Function.prototype.call (на самом деле мы можем использовать Date.call для сохранения еще большего количества байтов, но это менее семантически).

Мы также можемиспользуйте нашу переменную 'a', поскольку ее прототип содержит ссылку на String.prototype.toUpperCase , которую мы можем поменять с помощью a.toUpperCase.Это комбинация из трех решений, приведенных выше, и этих мер по экономии байтов - вот как мы получаем код в верхней части этого поста.

5 голосов
/ 26 февраля 2018

Старый пост, но стоит расширить ответ @ChaosPandion для других случаев использования с более ограниченным RegEx. Например. убедитесь, что (f) или группа захвата группы имеют определенный формат /z(f)oo/:

> a="foobazfoobar"
'foobazfoobar'
> a.replace(/z(f)oo/, function($0,$1) {return $0.replace($1, $1.toUpperCase());})
'foobazFoobar'
// Improve the RegEx so `(f)` will only get replaced when it begins with a dot or new line, etc.

Я просто хочу выделить два параметра function, позволяющих найти определенный формат и заменить группу захвата в этом формате.

1 голос
/ 01 декабря 2018

РЕШЕНИЕ

a.replace(/(f)/,x=>x.toUpperCase())  

для замены всех вхождений группы используйте /(f)/g регулярное выражение.Проблема в вашем коде: String.prototype.toUpperCase.apply("$1") и "$1".toUpperCase() дает "$1" (попробуйте в консоли самостоятельно) - так что это ничего не меняет и фактически вы дважды вызываете a.replace( /(f)/, "$1") (что также ничего не меняет).

let a= "foobar";
let b= a.replace(/(f)/,x=>x.toUpperCase());
let c= a.replace(/(o)/g,x=>x.toUpperCase());

console.log("/(f)/ ", b);
console.log("/(o)/g", c);
0 голосов
/ 24 февраля 2019

Учитывая словарь (объект, в данном случае, Map) свойства, значения и использование .bind(), как описано в ответах

const regex = /([A-z0-9]+)/;
const dictionary = new Map([["hello", 123]]); 
let str = "hello";
str = str.replace(regex, dictionary.get.bind(dictionary));

console.log(str);

Использование простого объекта JavaScript и функции, определенной для получения возвращаемого значения свойства объекта или исходной строки, если совпадение не найдено

const regex = /([A-z0-9]+)/;
const dictionary = {
  "hello": 123,
  [Symbol("dictionary")](prop) {
    return this[prop] || prop
  }
};
let str = "hello";
str = str.replace(regex, dictionary[Object.getOwnPropertySymbols(dictionary)[0]].bind(dictionary));

console.log(str);
...