Я хотел бы знать, как заменить группу захвата прописными буквами в JavaScript. Вот упрощенная версия того, что я пробовал до сих пор, но не работает:
> a="foobar"
'foobar'
> a.replace( /(f)/, "$1".toUpperCase() )
'foobar'
> a.replace( /(f)/, String.prototype.toUpperCase.apply("$1") )
'foobar'
Не могли бы вы объяснить, что не так с этим кодом?
javascript
regex
replace
uppercase
Эван Кэрролл
источник
источник
Ответы:
Вы можете передать функцию в
replace
.var r = a.replace(/(f)/, function(v) { return v.toUpperCase(); });
Объяснение
a.replace( /(f)/, "$1".toUpperCase())
В этом примере вы передаете строку функции замены. Поскольку вы используете специальный синтаксис замены ($ N захватывает N-й захват), вы просто задаете то же значение. На
toUpperCase
самом деле это обман, потому что вы делаете только верхний регистр строки замены (что несколько бессмысленно, потому что символы$
и один1
не имеют верхнего регистра, поэтому возвращаемое значение все равно будет"$1"
) .a.replace( /(f)/, String.prototype.toUpperCase.apply("$1"))
Вы не поверите, но семантика этого выражения абсолютно одинакова.
источник
$1
первую группу захвата.Я знаю, что опаздываю на вечеринку, но вот более короткий метод, который больше соответствует вашим первоначальным попыткам.
a.replace('f', String.call.bind(a.toUpperCase));
Так в чем же вы ошиблись и что это за новое вуду?
Проблема 1
Как указывалось ранее, вы пытались передать результаты вызываемого метода в качестве второго параметра String.prototype.replace () , тогда как вместо этого вы должны передать ссылку на функцию
Решение 1
Это достаточно легко решить. Простое удаление параметров и скобок даст нам ссылку, а не выполнение функции.
a.replace('f', String.prototype.toUpperCase.apply)
Проблема 2
Если вы попытаетесь запустить код сейчас, вы получите сообщение об ошибке, в котором говорится, что undefined не является функцией и поэтому не может быть вызван. Это связано с тем, что 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))
Сбрасывание байтов!
Никто не хочет набирать прототип много раз. Вместо этого мы воспользуемся тем фактом, что у нас есть объекты, которые ссылаются на одни и те же методы через наследование. Конструктор String, будучи функцией, наследуется от прототипа функции. Это означает, что мы можем заменить в String.call Function.prototype.call (на самом деле мы можем использовать Date.call, чтобы сохранить еще больше байтов, но это менее семантично).
Мы также можем использовать нашу переменную 'a', поскольку ее прототип включает ссылку на String.prototype.toUpperCase, мы можем заменить ее на a.toUpperCase. Это комбинация трех вышеперечисленных решений и этих мер по экономии байтов - вот как мы получаем код в верхней части этого поста.
источник
Старый пост, но стоит расширить ответ @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
позволяющих найти конкретный формат и заменить группу захвата в этом формате.источник
return $0.replace($0, $1.toUpperCase())
, где$0
первый аргументПочему бы нам просто не посмотреть определение ?
Если мы напишем:
a.replace(/(f)/, x => x.toUpperCase())
мы могли бы просто сказать:
a.replace('f','F')
Хуже того, я подозреваю, что никто не понимает, что их примеры работали только потому, что они захватывали все регулярное выражение с круглыми скобками. Если вы посмотрите на определение , первый параметр, переданный
replacer
функции, на самом деле является всем совпадающим шаблоном, а не шаблоном, который вы записали в круглых скобках:function replacer(match, p1, p2, p3, offset, string)
Если вы хотите использовать обозначение стрелочной функции:
a.replace(/xxx(yyy)zzz/, (match, p1) => p1.toUpperCase()
источник
РЕШЕНИЕ
a.replace(/(f)/,x=>x.toUpperCase())
для замены всех вхождений группы используйте
/(f)/g
regexp. Проблема в вашем коде: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);
источник
Учитывая словарь (объект, в данном случае a
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);
источник