Замініть групу захоплення регулярних виразів на верхній регістр у Javascript


81

Я хотів би знати, як замінити групу захоплення на верхній регістр у JavaScript. Ось спрощена версія того, що я намагався досі, але не працює:

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

Поясніть, що не так з цим кодом?


@Erik не видаляє компонент запитання. Я хочу знати, чому мій код теж не працює.
Еван Керролл,

Еване, я думав, що з повагою ставлюся до твого питання. Я видаляв лише те, що здавалося непотрібним. Оскільки ви дали код, який ви намагалися, і він, очевидно, не працював, то люди неявно знали, що вам потрібно пояснити, чому без цього потрібно говорити (і незграбно). Просто намагаюся допомогти! :)
ErikE

1
Еване, чи це краще? Я не хочу дратувати. Якщо ви повернетеся назад, я більше не редагуватиму, але чи могли б ви принаймні зберегти редагування заголовка та тегів?
ErikE

Технічно я взагалі не використовую Javascript, я використовую v8 (ECMAScript). Але, я думаю, що більшість людей, які шукають це, будуть шукати JavaScript, тому мені це добре.
Еван Керролл,

1
Не соромтеся додавати теги назад, якщо вважаєте, що вони належать.
ErikE

Відповіді:


159

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

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

Пояснення

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

У цьому прикладі ви передаєте рядок функції replace. Оскільки ви використовуєте спеціальний синтаксис заміни ($ N захоплює N-те захоплення), ви просто надаєте однакове значення. Це toUpperCaseнасправді обманює, тому що ви робите лише рядок заміни у верхньому регістрі (що дещо безглуздо, оскільки символи $та один 1не мають великих регістрів, тому повернене значення все одно буде "$1") .

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

Вірте чи ні, семантика цього виразу абсолютно однакова.


@ Еван Керролл: Будь ласка, перегляньте мою відповідь.
kay

4
Ах, я розумію, що ти маєш на увазі, я проговорюю "\ $ 1". Не результат вуду, який замінить, зробить це, мабуть, замінює $1першу групу захоплення.
Еван Керролл,

@EvanCarroll для детального пояснення, чому ваш початковий код не працював і як змусити його працювати, див. Мою відповідь нижче.
Джошуа Піккарі

15

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

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. Саме комбінація 3-х рішень, наведених вище, та цих заходів щодо економії байтів, є тим, як ми отримуємо код у верхній частині цього повідомлення.


4
Ви зберегли 8 символів, затуляючи код таким чином, що вимагає сторінки пояснення щодо більш очевидного рішення. Я не впевнений, що це перемога.
Лоуренс Дол

1
В інтелектуальному плані це чудове рішення, оскільки воно викладає / навчає щось про функції JavaScript. Але я з Лоуренсом, що на практиці це занадто неясно, щоб насправді його використовувати. Все ще круто.
meetamit

9

Стара публікація, але варто розширити відповідь @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завдяки яким пошук конкретного формату та заміна групи захоплення у форматі можливі.


Дякую! Попередні публікації, здається, дали відповідь на проблему, чому код ОП не працював, повністю пропускаючи те, що мені здавалося справжнім - заміна групи матчів!
Auspex

Я думаю, у вас є помилка у вашій функції заміни, але перевірте мене на цьому. Я думаю, це повинно бути return $0.replace($0, $1.toUpperCase()), де $0перший аргумент
Майк

Це був простий рядок для заміни рядків. тому f до F є правильним.
CallMeLaNN

Це дійсно корисно, якщо ви намагаєтесь замінити щось у дужках!
Diode Dan

3

Чому б нам просто не знайти визначення ?

Якщо ми пишемо:

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()

1

РІШЕННЯ

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

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


0

Дано словник (об’єкт, в даному випадку а 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);

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.