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

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

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

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

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

Q&A

解決済

1回答

1552閲覧

JavaScript、jQeuryで、正規表現に一致する文字列を抽出してテキストを書き換えたいです

mezamashiTV

総合スコア6

JavaScript

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

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

0グッド

0クリップ

投稿2020/04/01 10:23

編集2020/04/01 10:51

実現したいこと

文字列にタグがあった場合、条件に応じてその前後にスペースを入れたいです。

具体例としては以下strを与え、タグの前後にスペースを入れて、後述する目的の結果を得たいです。

var ARR = [ {name:'食パン',id:1}, {name:'あんぱん',id:2}, {name:'ごまあんぱん',id:3} ]; const str = "こんにちは。食パンと#チーズケーキが食べたいです!あとごまあんぱんも!"; const result = get(str); console.log('result',result);

###目的の結果
基本的にタグの前後にはスペースを入れますが、条件は「『変数patternの正規表現で指定した文字列』がタグの前にあった場合は、タグの前にはスペースは入れない』というものです。

なので、patternとして『。』が指定されているために、「こんにちは。」の次にある「#食パン」の前にはスペースが入っておらず、それ以外のタグにだけスペースが入り以下をresultとして得たい考えです。

こんにちは。#食パン と#チーズケーキが食べたいです!あと #ごまあんぱん も!

発生している問題

resultが以下になってしまいます。つまりスペースを入れる位置が問題です。

こんにちは。#食パンと#チーズケーキが食べたいです!あと#ご まあんぱんも!

該当のソースコード

var ARR = [ {name:'食パン',id:1}, {name:'あんぱん',id:2}, {name:'ごまあんぱん',id:3} ]; const str = 'こんにちは。食パンと#チーズケーキが食べたいです!あとごまあんぱんも!'; const result = get(str); console.log('result',result); function get( str ){ let base_str = str; // タグリストをsortしてregにする let terms_name_arr = ARR.map( (el)=> el.name ); console.log('terms_name_arr',terms_name_arr); // あんぱんとごまあんぱんならごまあんぱんを優先させたいのでsortしておく terms_name_arr = terms_name_arr.sort(function(a, b) {return a.length < b.length;}); const reg = new RegExp( terms_name_arr.join('|'), 'g'); console.log('reg:',reg); // スペースを入れる位置 let posi_arr = []; // タグ化 str = base_str.replace(reg, function(match, offset, string) { console.log('match=「'+match+'」'); console.log('offset=「'+offset+'」'); // スペースを入れる位置 posi_arr.push( offset ); posi_arr.push( offset+match.length+1 ); // 先頭に#がついてなければ付与 if( base_str.substr(offset-1,1) === '#' ){ return match; }else{ return '#' + match; } }); // スペースを入れる console.log('posi_arr',posi_arr); posi_arr.forEach(function(posi,i){ // ループのたびにスペースが入ってstrの文字数が変わるから i を加算 const x = posi + i + 1; console.log('x=「'+x+'」'); // スペースを入れる位置で前後に分ける const before = str.slice(0, x); const after = str.slice(x); // タグの前後がpatternに一致しなければ、タグの前後にスペースを挿入 const pattern = /[##。、,!!]/g; const before_last = before.substr(-1, 1); // タグの直前の文字 console.log('before_last',before_last); let insert; if( before_last.match(pattern) ){ insert = ''; }else{ insert = ' '; } str = before + insert + after; }); // 一番最初のスペースだけ削除 return str.replace(/^\s+/,""); }

試したこと

「forEach」によって「str」が常に変化してしまうことが原因ではないかと考えています。

そしてスペースの挿入位置「x」を計算するときに、この「str」の変化を踏まえるべく「i」という値を加算しているのですが、それだけでは不備があるようです。

そこで「// スペースを入れなければ「i」を加算しない」という条件を付けてみたいのが以下ですが、このアプローチではうまくいきませんでした。

// スペースを入れる let add = true; console.log('posi_arr',posi_arr); posi_arr.forEach(function(posi,i){ // ループのたびにスペースが入ってstrの文字数が変わるから i を加算 let x; if(add){ x = posi + i + 1; }else{ x = posi + 1; } console.log('x=「'+x+'」'); // スペースを入れる位置で前後に分ける const before = str.slice(0, x); const after = str.slice(x); // タグの前後がpatternに一致しなければ、タグの前後にスペースを挿入 const pattern = /[##。、,!!]/g; const before_last = before.substr(-1, 1); // タグの直前の文字 console.log('before_last',before_last); let insert; if( before_last.match(pattern) ){ insert = ''; add = false; // スペースを入れなければ「i」を加算しない }else{ insert = ' '; add = true; } str = before + insert + after; });

その他、ご覧のようにコンソールを出しながら、あれやこれやとスペースを入れる「x」の位置を調整しようと試しているのですが、あちらを調整すればこちらがズレる、という繰り返しをもう今朝から100回くらい重ねておりまして…

途方にくれておりましたところ、質問サイトの利用に思い至りました。

補足情報(FW/ツールのバージョンなど)

特にバージョン情報は関係ないかと思いますが、jQueryは最新の3.4での方法でお願いできましたら幸いです。

何卒、宜しくお願いいたします。

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

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

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

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

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

yambejp

2020/04/01 10:50 編集

idってなにかに使いますか? それとキーワードを順に評価するのであれば 「ごまあんぱん」という文字は先に「あんぱん」にヒットしますので 「ごま #あんぱん」にしかなりえないと思いますが?
mezamashiTV

2020/04/01 10:50

こちらの質問の流れでは使いませんが、ARRは使いまわす値なので他のところでidが必要となります。
mezamashiTV

2020/04/01 10:58

「ごまあんぱん」の件は仰る通り「あんぱん」より「ごまあんぱん」をヒットさせたいです。そのため文字が長い順でsortしているという流れです。
guest

回答1

0

ベストアンサー

こんなかんじですかね?

投稿2020/04/01 11:13

yambejp

総合スコア116724

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

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

yambejp

2020/04/01 11:14 編集

var ARR = [ {name:'食パン',id:1}, {name:'あんぱん',id:2}, {name:'ごまあんぱん',id:3} ]; var reg=new RegExp("(^|.)("+ARR.map(x=>x.name).sort((x,y)=>x.length<y.length).join('|')+")+","g"); const str = "こんにちは。食パンと#チーズケーキが食べたいです!あとごまあんぱんも!"; const result = str.replace(reg,x=>x.substring(0,1)+(x.substring(0,1)=="。"?"":" ")+" #"+x.substring(1)+" "); console.log(result);
yambejp

2020/04/01 11:15

※当初例示したsubstrは非推奨なのでsubstringに書き換えました
mezamashiTV

2020/04/01 11:28 編集

早速のご回答、誠にありがとうございます。そんなに短くなるとは…自分のコードが笑えてきました。regの作り方など感動です。 さてご回答は以下の結果となるようです。 (このコメント欄の都合上、「スペース」を「_」で表記します。) ``` こんにちは。_#食パン_と#チーズケーキが食べたいです!あと__#ごまあんぱん_も! ``` ですが、「_#食パン_」でなく「#食パン_」で考えております。「。」の後にあるタグなので前のスペースを入れないという条件があるためです。 そして、「__#ごまあんぱん_」ではなく「_#ごまあんぱん_」のようにスペースが1つという考えでした。 あと、patternがどこかにいってしまったという点もあり、恐れながら目的に一歩及びませんでしたが、自分の冗長なコードを大いに反省する機会となりました。どうもありがとうございます。
yambejp

2020/04/01 11:41 編集

先頭と末尾にキーワードが来たときどうしますか? それとキーワードが続いた場合はどうなりますか? とりあえず考えなくて大丈夫でしょうか?
mezamashiTV

2020/04/01 11:47

>先頭 先頭に来たときは前のスペースは不要です。 質問の最後の行の return str.replace(/^\s+/,""); がその意図でした。 >末尾 末尾なら後ろのスペースが不要です。申し訳ございません、完全に失念しておりました。ご指摘ありがとうございます。 >続いた場合 こちらも失念しておりました。重ねてお礼申し上げます。続いた場合、間のスペースは一つとしたいです。 たとえば var str = "食パンあんぱん"; ならば console.log(result); // -> "#食パン_#あんぱん" が理想的です。
yambejp

2020/04/01 12:13

実はマッチ箇所が続くのが相当めんどくさいです・・・ 一旦保留でいいですか? var ARR = [ {name:'食パン',id:1}, {name:'あんぱん',id:2}, {name:'ごまあんぱん',id:3} ]; var reg=new RegExp("(^|.)("+ARR.map(x=>x.name).sort((x,y)=>x.length<y.length).join('|')+")(.|$)","g"); const str = "食パンこんにちは。食パンと#チーズケーキが食べたいです!あとごまあんぱんも!食パン"; var res=str.replace(reg,x=>(r=x.match(new RegExp(reg.source))) && r[1]+(["","。"].indexOf(r[1])>=0?"":"_")+"#"+r[2]+(r[3]==""?"":"_")+r[3]); console.log(res);
yambejp

2020/04/01 12:13

結果: 「食パンこんにちは。食パンと#チーズケーキが食べたいです!あとごまあんぱんも!食パン」 ↓↓↓ 「#食パン_こんにちは。#食パン_と#チーズケーキが食べたいです!あと_#ごまあんぱん_も!_#食パン」
mezamashiTV

2020/04/01 13:27

とりあえず「。」へのご対応ということですね。どうもありがとうございます。大変高レベルなコードに触れるまたとない機会となりました。 初心者なりのアレンジで恐縮ですが、「。」以外へも対応させるとしたら以下の感じではいかが思われますでしょうか? 「。」の部分を「pattern」に置き換えればできたと思ったのですが、変ですか? var ARR = [ {name:'食パン',id:1}, {name:'あんぱん',id:2}, {name:'ごまあんぱん',id:3} ]; var reg=new RegExp("(^|.)("+ARR.map(x=>x.name).sort((x,y)=>x.length<y.length).join('|')+")(.|$)","g"); const str = "食パンこんにちは!食パンと#チーズケーキが食べたいです!あとごまあんぱんも!食パン"; const pattern = ["","。","、","!","!"]; var res=str.replace(reg,x=>(r=x.match(new RegExp(reg.source))) && r[1]+(pattern.indexOf(r[1])>=0?"":"_")+"#"+r[2]+(r[3]==""?"":"_")+r[3]); console.log(res);
yambejp

2020/04/01 13:31

> 「。」の部分を「pattern」に置き換えれば それでできてると思いますが、なにか想定と違いますか?
mezamashiTV

2020/04/01 13:38

いえ想定通りと思うのですが、私にとって見たこともないようなレベルのコードでしたので、それに対して生兵法のアレンジをしてしまい不安に感じておりました。 大変すばらしいコードをどうもありがとうございました。また機会がございましたらそのときはどうぞ宜しくお願いいたします。
mezamashiTV

2020/04/02 02:55 編集

いつかの自分へのメモ用 以下のように「食パン」でなく「食パン(はちみつ味)」などの場合、r[3]が取得できなくなるので var ARR = [ {name:'食パン(はちみつ味)',id:1}, {name:'あんぱん',id:2}, {name:'ごまあんぱん',id:3} ]; 以下のように正規表現をエスケープする関数を作り function escapeRegExp(string) { return string.replace(/[\^$.*+?()[]{}|]/g, '\$&'); } 以下の部分は .map(x=>x.name) 以下のようにエスケープしてあげればOK .map(x=>escapeRegExp(x.name))
yambejp

2020/04/02 03:12 編集

正規表現は予約語(記号)がたくさんあるので、 とくにユーザーからの入力語をヒットさせる場合 逐次エスケープ処理が必要ですね、
mezamashiTV

2020/04/02 03:40

かっこをつけた途端できなくなり苦労しました。正規表現は見にくいので大変ですよね。良い経験ができてよかったです。
mezamashiTV

2020/04/02 04:36

いつかの自分へのメモ用 ~その2~ 以下strの「、#食パン(はちみつ味)_」のように元から「タグの形式」があった場合は処理をしたくないので、 var str = "食パンこんにちは!食パンと#チーズケーキが食べたいです!あとごまあんぱんと、#食パン(はちみつ味) も!食パン"; それを除外するためにis_tag()で判定し、最終的なコードは以下でOK https://jsfiddle.net/s4qrcgya/
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問