web-dev-qa-db-ja.com

RegexキャプチャグループをJavascriptの大文字に置き換えます

JavaScriptでキャプチャグループを大文字に置き換える方法を知りたいです。以下は、これまで試したものの、機能しない簡易版です。

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

このコードの何が問題なのか説明していただけますか?

67
Evan Carroll

関数をreplaceに渡すことができます。

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

説明

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

この例では、文字列を置換関数に渡します。特別な置換構文($ NはN番目のキャプチャを取得します)を使用しているため、単に同じ値を指定しています。 toUpperCaseは実際には欺かれています。なぜなら、置換文字列を大文字にしているだけだからです($と1つの1文字には大文字がないため、戻り値は引き続き"$1"

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

信じられないかもしれませんが、この式のセマンティクスはまったく同じです。

140
ChaosPandion

私はパーティーに遅れていることは知っていますが、ここでは最初の試みに沿った短い方法を紹介します。

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

それで、あなたはどこで間違ったのですか、そしてこの新しいブードゥー教は何ですか?

問題1

前に述べたように、呼び出したメソッドの結果を String.prototype.replace() の2番目のパラメーターとして渡そうとしましたが、代わりに関数への参照を渡す必要があります

解決策1

これは簡単に解決できます。パラメータと括弧を削除するだけで、関数を実行するのではなく、参照が得られます。

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

問題2

ここでコードを実行しようとすると、undefinedは関数ではないため呼び出しできないことを示すエラーが表示されます。これは、String.prototype.toUpperCase.applyが実際にはJavaScriptのプロトタイプ継承を介した Function.prototype.apply() への参照であるためです。実際に私たちがやっていることは、このように見えます

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

これは明らかに我々が意図したものではありません。 Function.prototype.apply() on String.prototype.toUpperCase() を実行することをどのように知っていますか?

解決策2

Function.prototype.bind() を使用すると、特にコンテキストをString.prototype.toUpperCaseに設定したFunction.prototype.callのコピーを作成できます。次のものがあります

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

問題3

最後の問題は、 String.prototype.replace() が置換関数にいくつかの引数を渡すことです。ただし、 Function.prototype.apply() は、2番目のパラメーターが配列であることを期待しますが、代わりに文字列または数値を取得します(キャプチャグループを使用するかどうかによって異なります)。これにより、無効な引数リストエラーが発生します。

解決策3

幸いにも、 Function.prototype.apply()Function.prototype.call() (任意の数の引数を受け入れ、型制限はありません)で簡単に置き換えることができます=。作業コードに到達しました!

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

バイトを流します!

だれもprototypeを何度も入力する必要はありません。代わりに、継承を介して同じメソッドを参照するオブジェクトがあるという事実を活用します。関数であるStringコンストラクターは、Functionのプロトタイプを継承します。これは、String.callで Function.prototype.call に置き換えることができることを意味します(実際、Date.callを使用してさらに多くのバイトを保存できますが、セマンティックではありません)。

プロトタイプには String.prototype.toUpperCase への参照が含まれているため、変数 'a'を活用することもできます。これはa.toUpperCaseと交換できます。上記の3つのソリューションとこれらのバイト節約対策の組み合わせが、この投稿の冒頭でコードを取得する方法です。

11
Joshua Piccari

以前の投稿ですが、@ ChaosPandionの回答を拡張して、より制限されたRegExを使用する他のユースケースに投稿する価値があります。例えば。特定の形式/z(f)oo/(f)またはキャプチャグループサラウンドを確認します。

> 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の2つのパラメーターを強調したいので、特定の形式を見つけて、可能な形式内のキャプチャグループを置き換えます。

5
CallMeLaNN

[〜#〜] solution [〜#〜]

_a.replace(/(f)/,x=>x.toUpperCase())  
_

すべてのgrupオカレンスを置き換えるには、/(f)/g regexpを使用します。あなたのコードの問題:String.prototype.toUpperCase.apply("$1")"$1".toUpperCase()は_"$1"_を与えます(自分でコンソールで試してください)-何も変更せず、実際には2回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);_
1

回答で説明されているように、プロパティ、値のディクショナリ(オブジェクト、この場合は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);
0
guest271314