質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

置換

置換とは文字列中の特定の文字に対して、別の文字列に置き換えることを指します。

Q&A

解決済

4回答

1118閲覧

正規表現にて、「山梨県」中以外の「梨」の置換について

devto

総合スコア1

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

置換

置換とは文字列中の特定の文字に対して、別の文字列に置き換えることを指します。

0グッド

4クリップ

投稿2021/09/05 00:55

編集2021/09/05 05:35

javascriptのreplaceにて、正規表現を用いて下記のような置換を行いたい。

文章中の文字列A(例:梨)の中から、文字列B(例:山梨県)に含まれないものだけを置換する際の正規表現を作成したい。

(例:「特に山梨県の梨はみずみずしくて美味い」⇒「特に山梨県のりんごはみずみずしくて美味い」)

ただし、文字列A、文字列Bについては動的に生成する文字列であるため、「~(文)~(字)~(列)~」のように文字列を分割するのではなく、~(文字列)~のような形式としたい。

現状、「山梨県」を一時的に「山○県」に置換⇒「梨」をりんごに置換⇒「山○県」を「山梨県」に置換の形で実現済みですが、正規表現を用いてreplace一発で置換を行えないか、方法があればご教示ください。


追記
text(元文章、仮に「特に山梨県の梨はみずみずしくて美味い」)
beffor(置換の対象とする文字列A、仮に「梨」)
exception(置換の例外とする文字列B、仮に「山梨県」)
after(置換後の文字列、仮に「りんご」)
と与えられた際に、「(?<!山)梨(?!県)」のようにパラメータの内容を考慮した属パラメータ的なものではなく、
「new RegExp("???"+(beffor)+"???"+(exception)+"???")」のように各パラメータの内容や文字構成に依存しない汎用的な正規表現を目指しています。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2021/09/05 01:14

文字列Aと文字列Bに、文字数や位置などの条件はありますか?
Daregada

2021/09/05 01:17

「山梨」や「梨県」は、それぞれ「山りんご」と「りんご県」に置換したいのでしょうか。それとも、置換したくないのでしょうか。
devto

2021/09/05 05:06

>文字列Aと文字列Bに、文字数や位置などの条件 元文章、文字列A、文字列Bについて、文字数、位置等の構成条件は無い状態での実現を目指しています。 >「山梨」や「梨県」 「山梨県」に含まれる「梨」ではないのでそれぞれ「山りんご」「りんご県」とすることを目指しています。
Zuishin

2021/09/05 07:37

> 「(?<!山)梨(?!県)」のようにパラメータの内容を考慮した属パラメータ的なものではなく、 「new RegExp("???"+(beffor)+"???"+(exception)+"???")」のように各パラメータの内容や文字構成に依存しない汎用的な正規表現を目指しています。 それはなぜですか? 一応私の回答はそれを満たすと思いますが、最終的に置換できればいいというものではないんでしょうか?
devto

2021/09/05 07:42

>Zuishinさん 回答見落としておりまして確認しておりました。 回答いただいた内容で問題なさそうですので、参考にさせていただきます。 ありがとうございました!
guest

回答4

0

ベストアンサー

「山梨県」以外にも例外が増えることを考えて次のようなものはどうでしょうか?

js

1const src = "特に山梨県の梨はみずみずしくて美味い"; 2 3const dst = src.replaceAll(/山梨県|梨/gu, a => a === "梨" ? "りんご" : a); 4console.log(dst);

例えば「山梨県」の他に「二十世紀梨」を対象にするのであれば次のように書き換えます。

js

1const dst = src.replaceAll(/山梨県|二十世紀梨|梨/gu, a => a === "梨" ? "りんご" : a);

以上を関数化すると次のようになります。

js

1const escapeRegularExpression = re => { 2 return re.replace(/[\^$*+?.(){[|]/g, a => `\${a}`); 3}; 4 5const f = (src, target, ignore, replacement) => { 6 const searchValue = ignore.concat(target).map(escapeRegularExpression).join("|"); 7 const regExp = new RegExp(searchValue, "gu"); 8 return src.replace(regExp, a => a === target ? replacement : a); 9}; 10 11const dst = f("特に山梨県の梨はみずみずしくて美味い", "梨", ["山梨県"], "りんご"); 12console.log(dst);

target や ignore にメタ文字が含まれておらず、無視する単語が一つということがどちらも保証されているなら、次のように簡単に書いてもいいでしょう。

js

1const f = (src, target, ignore, replacement) => { 2 return src.replace(new RegExp(`${ignore}|${target}`, "gu"), a => a === target ? replacement : a); 3};

投稿2021/09/05 02:59

編集2021/09/05 06:56
Zuishin

総合スコア28660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

もし、「山梨」と「梨県」に対しても置換を行なうのであれば、(?<!山)梨|梨(?!県)とします。いっぽう、「山梨」と「梨県」を置換対象から除外するのであれば、(他の回答者の回答にあるように)(?<!山)梨(?!県)とします。

以下、元はPythonのコードをJavascriptに変更し、動的に生成される文字列AとBに対応。

Javascript

1let target = "梨ね。特に山梨県の梨はみずみずしくて美味い。山梨の梨は梨の王様や!! (梨県さん)"; 2//let target = '梨 山梨県 山梨 梨県 梨梨梨梨\n梨'; 3let src = "梨"; 4let dst = "りんご"; 5let exc = "山梨県"; 6 7let pre = exc.substr(0, exc.indexOf(src)); 8let post = exc.substr(exc.indexOf(src) + src.length); 9 10// let pat1 = /(?<!山)梨|梨(?!県)/g; 11let pat1 = new RegExp("(?<!" + pre + ")" + src + "|" + src + "(?!" + post + ")", "g"); 12// let pat2 = /(?<!山)梨(?!県)/g; 13let pat2 = new RegExp("(?<!" + pre + ")" + src + "(?!" + post + ")", "g"); 14 15result1 = target.replace(pat1, dst); 16console.log(result1); 17 18result2 = target.replace(pat2, dst); 19console.log(result2);

result

1りんごね。特に山梨県のりんごはみずみずしくて美味い。山りんごのりんごはりんごの王様や!! (りんご県さん) 2りんごね。特に山梨県のりんごはみずみずしくて美味い。山梨のりんごはりんごの王様や!! (梨県さん)

投稿2021/09/05 01:25

編集2021/09/05 04:38
Daregada

総合スコア11990

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

think49

2021/09/05 03:04

命題通りならこちらの回答で良いと思います。
Zuishin

2021/09/05 03:57 編集

質問は JavaScript ですが、これ Python ですよね? また、次のように「梨」も「山梨県」も動的に生成されるようです。 > ただし、文字列A、文字列Bについては動的に生成する文字列であるため、「~(文)~(字)~(列)~」のように文字列を分割するのではなく、~(文字列)~のような形式としたい。 この回答では分割されているようですが、命題通りですか?
think49

2021/09/05 04:59 編集

> 質問は JavaScript ですが、これ Python ですよね? 正規表現の命題は満たしていると思います。 私の手元の環境で同じ正規表現に辿り着きました。 https://jsfiddle.net/79y2psw8/
think49

2021/09/05 05:02 編集

コメント後に @Zuishin さんのコメントが編集されたようなので、追記されたコメントに返信します。 > また、次のように「梨」も「山梨県」も動的に生成されるようです。 動的生成は読み落としでしたので、@Daregada さんの正規表現を動的生成するコードを書いておきました。 https://jsfiddle.net/12cjnbop/ 注意すべきは「正規表現メタキャラクタのエスケープ」「サロゲートペア文字の判別」の2点だと思います。
guest

0

質問者さんの要件は、以下の3点

  • 言語としては、javascript を使う。
  • replaceメソッドにて、正規表現を使う。
  • '山梨県' に含まれる '梨' だけを置換対象から外す。(したがって例えば、'山梨の天気'は置換する。)

をすべて満たすことと解釈しますと、こないな感じでどうでっしゃろ?

javascript

1const textBefore = '山梨県 山梨の天気 美味い梨 山梨県 山梨の天気 美味い梨'; 2 3// "山梨県" の中の "梨" のみを置換対象から外す 4const textAfter = textBefore.replace( 5 /(山)?梨(県)?/g, 6 (m, p1, p2) => p1 && p2 ? m : `${p1 || ''}りんご${p2 || ''}` 7); 8 9console.log(textAfter); // => "山梨県 山りんごの天気 美味いりんご 山梨県 山りんごの天気 美味いりんご"

サンプル

動的にしたもの

置換対象()、置換後(りんご)、置換対象外(山梨県) の各文字列を受け取る関数にしました。

function replaceWithout(src, from, to, excluding) { const tokens = excluding.split(from).filter(e => e); if (tokens.length !== 2) { throw new Error(`excluding の中に、from が1回だけ出現しかつその from の前後に1文字以上の文字が存在する必要があります。`); } const regexp = new RegExp(`(${tokens[0]})?${from}(${tokens[1]})?`, 'g'); return src.replace(regexp, (m, p1, p2) => p1 && p2 ? m : `${p1 || ''}${to}${p2 || ''}`); }

javascript

1const result = replaceWithout('山梨県 山梨の天気 美味い梨 ヤマ梨県 梨梨梨 山梨県', '梨', 'りんご', '山梨県'); 2 3console.log(result); // => "山梨県 山りんごの天気 美味いりんご ヤマりんご県 りんごりんごりんご 山梨県"

サンプル

いくつか補足しときます。

  • 例外 excluding の中に、from が1回だけ出現しかつその from の前後に1文字以上の文字が存在する必要があります。 を投げているとおり、これが満たされないと、

javascript

1const regexp = new RegExp(`(${tokens[0]})?${from}(${tokens[1]})?`, 'g');

が意図通り動作しないと思われます。

  • 質問の要件を満たすreplaceを実行させる正規表現としては、Daregadaさんの回答の、否定後読みと、否定先読みを使うほうが、正規表現だけでやっつけてしまえるので、この回答よりも良いと思います。

  • この回答は、「replace の第二引数に関数を渡せて、その関数に渡されてくる引数を使えば柔軟な変換が可能になる例」ぐらいの参考にしていただければええと思いますわ。

投稿2021/09/05 04:18

編集2021/09/05 06:41
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

think49

2021/09/05 05:23

その発想はなかった…。着想が面白いと思いました。 動的生成の手を入れれば、要件を満たせると思います。
Zuishin

2021/09/05 05:43 編集

p1 && p2 は m === "山梨県" と同じなので、基本的な発想は私の回答と同じです。 私は与えられた文字列をバラさなくて済むよう正規表現を工夫して `山梨県|梨` にしたというだけですね。
退会済みユーザー

退会済みユーザー

2021/09/05 06:39

think49さん、Zuishinさん コメントありがとうございます。正規表現を動的生成するコードを追記しました。
guest

0

/(?!<山)梨(?!県)/でしょうか。否定先読み、否定後読みです。

#追記訂正
こんな感じでしょうか。

JavaScript

1let a="梨、山梨県、梨、山梨、梨県、山梨県"; 2let x="山梨県"; 3let y="梨"; 4let z="りんご"; 5let r=new RegExp(`(?!${x})(?:^|(.{${x.indexOf(y)}}))${y}`,"g"); 6 7let b=a.replace(r,`$1${z}`); 8console.log(b);

/(?!山梨県)(?:^|(.{1}))梨/gのような正規表現を生成します。

#追記
と思ったけど、a="山梨県、、、、";x="大山梨県";の様なケースだと駄目ですね。

JavaScript

1let x="山梨県"; 2let y="梨"; 3let z="りんご"; 4let w=x.indexOf(y); 5let r= new RegExp(`(?!${x})(?:^(.{0,${w}})|(.{${w}}))${y}`,"g"); 6 7console.log(r); 8let b=a.replace(r,`$1$2${z}`); 9console.log(a); 10console.log(b);

質問文にお書きの、一度「山梨県」を別の文字列に置換する方法の方が明快で良いと思います。

投稿2021/09/05 01:12

編集2021/09/05 02:29
otn

総合スコア84533

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2021/09/05 01:17

それだと、「山梨は」のような片側だけ一致するパターンまで除外されてしまいませんか?
otn

2021/09/05 01:45 編集

> ただし、文字列A、文字列Bについては動的に生成する文字列であるため、 の意味が最初よく分からなかったのですが、わかったので、これでは駄目ですね。追記します。
think49

2021/09/05 03:06 編集

@otn さん 2021/09/05 11:29追記コードで "梨梨\n梨" を置換すると、"梨りんご\n梨" になります。
otn

2021/09/05 03:30

ああ、連続は駄目ですね。やっぱり無理か。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問