🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
JavaScript

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

Q&A

解決済

3回答

2281閲覧

JavaScriptによる文字列置換

Y.NINOMIYA

総合スコア32

JavaScript

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

1グッド

3クリップ

投稿2019/10/31 10:30

編集2019/11/03 01:53

JavaScriptにおいて文字列の◯番目から△番目を他の文字列に置換させたくて調べていたのですが、やり方がどうも見つかりません。特定の文字列を検索して置換する方法はいくつもヒットするのですが指定された位置から指定範囲を置換する方法が見つかりませんでした。
そもそものそのような関数はないのでしょうか。最初は指定箇所以外を切り取って別で連結させる方法をとっっていたのですが非効率な気がし、やり方を調べていて今に至った形です。

回答よろしくお願いします

補足
質問中の“非効率なやり方”とは

JavaScript

1var str = "Hello,World!"; 2console.log(str); 3var str_1 = str.substr(0, 6) //"Hello"を切り出し 4var str_2 = str.substr(11, 1); //"!"を切り出し 5str = str_1 + "Japan" + str_2; 6console.log(str); //“Hello,Japan!”と出力

以上のようなコードでした。

また、位置の指定による置換をしたいのは、置換したい文字列が全体に複数ある場合に対応するため、また、ある程度規則性がある文字列の中で置換するので開始位置が変わらないという点からこのような手段を取りたかったのです

退会済みユーザー👍を押しています

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

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

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

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

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

Zuishin

2019/10/31 11:01

その非効率なやり方を書いてください。それがわからなければ、それを回答する人も出てくるかもしれません。
Y.NINOMIYA

2019/11/01 04:03

var str = "Hello,World!"; console.log(str); var str_1 = str.substr(0, 6) //"Hello"を切り出し var str_2 = str.substr(11, 1); //"!"を切り出し str = str_1 + "Japan" + str_2; console.log(str); //“Hello,Japan!”と出力 このような形でやっていました
jun68ykt

2019/11/01 07:16

横から失礼します。上記の Y.NINOMIYA さんのコメントを拝読したうえで、当方の回答に追記4を記載しました。要点としては、文字列の◯番目から△番目を他の文字列に置換するという手段をとらずに、 replace を使えばよいのでは?ということになります。
think49

2019/11/01 10:36 編集

質問者の期待する要件は「規則性のある固定長文字列から指定範囲の文字列を置換する」想定です。 私が同等の機能を求めたときにはそれでした。 「やりたいこと」の「意図」や「先にある目的」を明らかにすると別案が出てくるかもしれませんね。
Y.NINOMIYA

2019/11/01 13:18

置き換えたい文字列が全体に複数ある場合に対応するため、また、全体の文字列もある程度規則性があり、置換の開始位置はそうそう変わらないので「文字列の◯番目から△番目を置換する」という方法を取りたかったのです。
Zuishin

2019/11/01 13:26

なんとなくですが、配列を使うべき案件の気がしますね。 変数名の連番の置換とかその類のにおいを感じます。
kei344

2019/11/02 04:23

(質問文は編集できます)この「質問への追記・修正の依頼」の部分はデフォルトで表示されませんので、質問本文に追記することをお勧めします。
think49

2019/11/02 16:34 編集

To: @Y.NINOMIYA さん > 置き換えたい文字列が全体に複数ある場合に対応するため、 指摘すべき内容は回答に追記しましたが、回答の根底を覆す追加条件をさらっとコメントで追記するだけで済ますのは止めて下さい。 今までの回答が無駄になりますし、今後回答する人がコメントを読まなければ無駄になります。 kei344さんが指摘されているように質問を編集して追記するのが良いと思います。 Zuishinさんの指摘を受けて追記したコードも重要な情報です。
think49

2019/11/02 16:44 編集

To: @Y.NINOMIYA さん 回答に反応がないのは「期待通りの回答が得られなかったから」でしょうか。 それならば、「その回答のどこが自身の意図にそぐわなかったのか」を各回答のコメントで返信して下さい。 また、質問を [編集] して正しく意図が伝わる文章/コードに修正してください。 過去質問も回答がついていながら放置されているようですので、同様に対応して下さい。 https://teratail.com/search?q=author%3AY.NINOMIYA+sort%3Acreated-desc
Y.NINOMIYA

2019/11/03 01:59

申し訳ありませんでした。 質問編集、過去質問対応しました。
guest

回答3

0

ベストアンサー

String.prototype.splice

目的がはっきりしているのであれば、String.prototype にあるメソッドを一つずつ確認すれば良いでしょう。

結論をいえば、ES2019に String.prototype.splice はありません。
ご自身でも確認してみてください。

String.prototype.replace

一応、正規表現を駆使すれば、一つのメソッドで実現は可能です。

JavaScript

1/** 2 * レガシーコード 3 */ 4'Hello, World!'.replace(/^([\s\S]{7})[\s\S]{5}/, '$1JavaScript'); // "Hello, JavaScript!" 5 6/** 7 * ES2018コード 8 */ 9'Hello, World!'.replace(/(?<=^.{7}).{5}/s, 'JavaScript'); // "Hello, JavaScript!"

String.prototype.slice

JavaScript

1const string = 'Hello, World!'; 2 3string.slice(0,7) + 'JavaScript' + string.slice(12); // "Hello, JavaScript!"

CharacterData.prototype.replaceData

DOM APIに目的の関数があるようですね。

JavaScript

1const text = new Text('Hello, World!'); 2text.replaceData(7, 5, 'JavaScript'); 3text.data; // "Hello, JavaScript!"

ユーザ定義関数(テキストノードをキャッシュ)

JavaScript

1const replaceData = (text => 2 (string, offset, count, data) => (text.data = string, text.replaceData(offset, count, data), text.data) 3)(new Text); 4 5replaceData('Hello, World!', 7, 5, 'JavaScript');

複数の範囲指定に対応する

置き換えたい文字列が全体に複数ある場合に対応するため、また、全体の文字列もある程度規則性があり、置換の開始位置はそうそう変わらないので「文字列の◯番目から△番目を置換する」という方法を取りたかったのです。

置換する範囲が複数ある場合、効率化の為には複数をまとめて一括置換する必要があり、回答の方向性が大きく変わります。
今までに出てきた回答は全て一つの範囲を置換するコードであり、要件を満たしていません。

JavaScript

1replaceByIndex('ABC', [0, 1, 'AAA'], [1, 1, 'BBB'], [2, 1, 'CCC']); // "AAABBBCCC"

課題となるのは、置換元/置換先の文字列長が同値かどうか、です。

JavaScript

1var str = "Hello,World!"; 2console.log(str); 3var str_1 = str.substr(0, 6) //"Hello"を切り出し 4var str_2 = str.substr(11, 1); //"!"を切り出し 5str = str_1 + "Japan" + str_2; 6console.log(str); //“Hello,Japan!”と出力

"World""JavaScript" の文字列長は異なるので、可変長の置換を期待しています。
何が問題かというと、1回目の置換後に2回目の置換処理を実行する場合、文字列長が変わるとインデックス値もずれるのです。
これは2つの解決法があります。

  • 置換前後の文字列長の差分をとって、インデックス値を補正する
  • 常に「置換前の文字列」から文字列を切り出す

replace-by-index.js では後者を採用しました。


なお、既に置換されている範囲を何度も検索してしまう非効率性は String#slice を使用する関係上、無視しています。
置換元文字列を slice で切り出せば対応可能ですが、インデックス値も補正しなければならず、諸々の補正処理を全て実行すると、パフォーマンスが落ちる可能性がありました(実験してはいないので、興味があれば検証してみて下さい)。

String#replace で正規表現指定する方法を採用すれば、1回ずつしか消費されない為、この問題に対応できます。
正規表現を動的生成し、一度に置換すれば要件を達成できるでしょう。
[\s\S] は効率が良いとはいえない為、s フラグを実装しているブラウザのみで問題がなければ、ですが。

実行速度の確認

「理論値」と「実測値」が異なる事は往々にしてあります。
今回、回答するにあたって実測値を基にしてはいない為、是非、実測して確認してみてください。

質問の仕方について

追加条件でコードの難易度が格段に上がりました。
回答の練り直しも余儀なくされましたので、今後は質問文に全ての条件を書くようにしてください。
また、補足要求を受けて追加情報は質問を [編集] して、質問文に追記してください。
全てのコメントを読んで質問の全容を読み取るのはそれなりに労力がかかります。

Re: Y.NINOMIYA さん

投稿2019/10/31 12:28

編集2019/11/02 16:42
think49

総合スコア18189

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

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

maisumakun

2019/11/01 00:57

DOMでテキスト操作ができるとは知らなかったです。
guest

0

いろいろやり方はあると思いますが、私が思いつくのは、
(1) 正規表現を使ったreplace
(2) 文字列を一文字づつの配列に変換し、配列のspliceメソッドを使用、その後文字列に戻す方法
です。(1)についてはthink49さんが書かれておられますので、(2)のコードを書きます。

javascript

1let str = 'This is an apple.'; 2let arr = [...str]; 3arr.splice(11, 5, 'orange'); 4console.log(arr.join('')); 5//=> This is an orange.

投稿2019/10/31 12:47

編集2019/10/31 12:50
shinji709

総合スコア805

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

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

0

こんにちは

配列だと splice() というメソッドが、そのような働きをしますが、文字列の spliceメソッドは無いです。なので、どうやって代替するのがよいか?ということになりますが、以下の投稿をみつけました。

上記の質問へのベストアンサー で挙げられている以下

javascript

1function spliceSlice(str, index, count, add) { 2 // We cannot pass negative indexes directly to the 2nd slicing operation. 3 if (index < 0) { 4 index = str.length + index; 5 if (index < 0) { 6 index = 0; 7 } 8 } 9 10 return str.slice(0, index) + (add || "") + str.slice(index + count); 11}

を使うか、もしくは頻繁に使うのであれば、上記の回答に対するコメント

You can add this globally with: String.prototype.splice = function (index, count, add) { return this.slice(0, index) + (add || "") + this.slice(index + count); } Just keep in mind it doesn't work exactly the same as the array splice method because strings are immutable (you can't modify them once created). So usage would be like: var s="ss"; s = s.splice(0,1,"t"); – William Neely Nov 18 '14 at 16:52

にあるように、

javascript

1String.prototype.splice = function (index, count, add) { return this.slice(0, index) + (add || "") + this.slice(index + count); }

とするのも一案として検討してよいかもしれません。

ただし、R.Mizukamiさんからコメントでご指摘がありましたように、 splice をそのままメソッド名に使うのは若干憚られるため、仮に replaceSubsequence としておき、以下、その使用例です。

以上、参考になれば幸いです。

追記1

Y.NINOMIYAさんのご質問の本文中に

そもそものそのような関数はないのでしょうか。最初は指定箇所以外を切り取って別で連結させる方法をとっっていたのですが非効率な気がし、やり方を調べていて今に至った形です。

とありましたが、上記に対する直接の回答としては、以下の二点です。

そのような関数はないのでしょうか。

答え: Stringの標準メソッドとして、そのものズバリのメソッドは用意されていないです。

最初は指定箇所以外を切り取って別で連結させる方法をとっっていたのですが非効率な気がし、

答え: 先述のstackoverflow のベストアンサーのコードも、そのような、「切り取って別で連結させる」ものですので、Y.NINOMIYAさんがご自身で書いた、一見「非効率な気が」するコードでよいのではと思いますが、それでもご自身のコードに確信が持てない場合は、そちらのコードを開示して、「このコードに非効率なところはありませんか?」というご質問をされると、より求めていらっしゃる回答が得られるかもしれません。

追記2

参考までに、先に挙げたstackoverflowのベストアンサー では、配列の splice を経由する、以下の spliceSplit

javascript

1function spliceSplit(str, index, count, add) { 2 var ar = str.split(''); 3 ar.splice(index, count, add); 4 return ar.join(''); 5}

と比較して、 置き換え後の文字列をslice で得られる前後の文字列で挟むように連結する(この回答の冒頭に挙げた) spliceSlice のほうがパフォーマンスが良かったことを、以下のようにパフォーマンスツールでの実測へのリンクを挙げて説明しています。

Here's a jsperf that compares the two and a couple other methods.

追記3

文字列を操作するための vocajs というライブラリがあります。Githubのレポジトリに現時点で、約2700のStarがついているので、それなりに支持されているようですが、このライブラリでは、(先ほど "若干憚られる" と書いた) splice という名前でご要望のメソッドが、以下のように提供されています。

v.splice(subjectopt='', start, deleteCountopt=subject.length-start, toAddopt='') → {string}

Changes subject by deleting deleteCount of characters starting at position start. Places a new string toAdd instead of deleted characters.

この Voca の spliceメソッドのソースコードを見てみると、以下

のように、やっていることは、先の spliceSlice と基本的には同じでした。

追記4

質問への追記・修正、ベストアンサー選択の依頼へのY.NINOMIYAさんの以下のコメント

Y.NINOMIYA 2019/11/01 13:03

var str = "Hello,World!";
console.log(str);
var str_1 = str.substr(0, 6) //"Hello"を切り出し
var str_2 = str.substr(11, 1); //"!"を切り出し
str = str_1 + "Japan" + str_2;
console.log(str); //“Hello,Japan!”と出力
このような形でやっていました

を拝読したので、追記します。

  • 文字列 var str = "Hello,World!" に含まれる部分文字列 "World""Japan" に置き換えて、 "Hello,Japan!" という文字列を得たい。

ということが課題であるとすると、この課題を解決するための手段として、ご質問にある

文字列の◯番目から△番目を他の文字列に置換

するという方法を選ぶこと自体が非効率かもしれません。(この場合の「非効率」とは「わざわざ面倒なやり方をしている」ぐらいの意味です。)

なぜなら

javascript

1var str_1 = str.substr(0, 6); 2var str_2 = str.substr(11, 1);

というコードを書こうとすると、611 という数を得るために World の直前と直後のインデクスを目視で数えなければならないからです。そのようなことをしなくても、 var str = "Hello,World!" に含まれる"World""Japan" に置き換えるには、 replaceを使って、以下で済みます。

javascript

1var str = "Hello,World!"; 2 3str = str.replace('World', 'Japan'); 4 5console.log(str);

Y.NINOMIYAさんとしては

文字列の◯番目から△番目を他の文字列に置換

するというやり方にこだわりがおありで、文字列に対して、 Array のsplice のような操作をするときの効率的なやり方をお求めなのか、または、 "World""Japan" に置き換えることが主目的なので、

文字列の◯番目から△番目を他の文字列に置換

するという方針も見直せる余地がおありなのかは、回答者には分からないところはありますが、"World""Japan" に置き換えたい、ということであれば、 611 を使わなくて済む

str.replace('World', 'Japan')

をお勧めしたいと思います。

追記5

質問への追記・修正、ベストアンサー選択の依頼へのコメントに、Y.NINOMIYAさんからのコメントで

Y.NINOMIYA 2019/11/01 22:18

置き換えたい文字列が全体に複数ある場合に対応するため、また、全体の文字列もある程度規則性があり、置換の開始位置はそうそう変わらないので「文字列の◯番目から△番目を置換する」という方法を取りたかったのです。

とありましたので、これをふまえて元のご質問にある

そもそものそのような関数はないのでしょうか。最初は指定箇所以外を切り取って別で連結させる方法をとっっていたのですが非効率な気がし、やり方を調べていて今に至った形です。

に返答します。

非効率な気がし

とのことですが、「文字列の◯番目から△番目を置換する」方法として、Y.NINOMIYA さんがお書きになった

javascript

1var str = "Hello,World!"; 2var str_1 = str.substr(0, 6) //"Hello"を切り出し 3var str_2 = str.substr(11, 1); //"!"を切り出し 4str = str_1 + "Japan" + str_2;

というコードが、アルゴリズムとして、特段、効率が悪いことをやっている、ということはないと思います。

ただ、

置き換えたい文字列が全体に複数ある場合に対応するため、また、全体の文字列もある程度規則性があり、置換の開始位置はそうそう変わらないので

とのことだったので、毎回、前方部分と後方部分を切り出して、置換後の文字列と連結するコードを書くのは煩わしい、ということはあると思うので、先に挙げた 、stackoverflow のベストアンサーに挙げられていた spliceSlice() のような関数を自作して使い回すか、あるいは自作も不要な策として、追記3で挙げた、vocajsのsplice を使ってみることを検討してみてもよいかもしれません。

以下は、vocajsのsplice を使って "Hello,World!" の "World" を "Japan" に置き換える例です。

投稿2019/10/31 11:48

編集2019/11/02 10:15
jun68ykt

総合スコア9058

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

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

R.Mizukami

2019/10/31 12:15

Array.prototype.splice() は元の配列を変更し、**削除された要素を持つ配列を返す** メソッドなので、別の名前にした方がいいかもしれません。
R.Mizukami

2019/10/31 12:35

すみません。書き方が悪かったですね。 言いたかったのは Array.prototype.splice() の方はメソッドの返り値が変更後の配列ではなく削除した要素のリストなので、回答に書かれたコードと(返り値の)挙動が違って紛らわしいかもしれませんよ、ということでした。
Zuishin

2019/10/31 12:48

返し方のことですね。読み違えました。すみません。
jun68ykt

2019/10/31 17:46

@R.Mizukami さん @Zuishin さん コメントありがとうございます。 確かに、R.Mizukamiさんの仰るとおりという気がしましたし、また、回答に挙げた stackoverflow のベストアンサーへのコメントにも spliceSlice and spliceSplit are not functionally equal due to Array.prototype.splice accepting negative indices: jsfiddle.net/sykteho6/5. – Martijn Jul 4 '16 at 13:35 というものがありましたので、上記の回答にある function spliceSlice や String.prototype.splice = ・・・ は、たとえば replaceSubstr あるいは、 replaceSubsequence replaceSubseq など、 replace何とか にしたほうがよさそうですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問