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

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

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

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

正規表現

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

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

Q&A

解決済

5回答

355閲覧

[JavaScript] 文字列分割関数の依頼 [正規表現]

ponsea

総合スコア15

JavaScript

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

正規表現

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

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

0グッド

1クリップ

投稿2017/08/27 06:18

編集2017/08/27 06:42

ある文字列を、文字列の配列へ分割する関数の実装に悩んでいます。

パターン1: "[?]" ごとに分割(?は任意の1文字) ※ []内に複数文字あるものは無視
input
"[あ] あいうえお [い] かきくけこ [う] さしすせそ [わをん] たちつてと"
output
[
"[あ] あいうえお",
"[い] かきくけこ",
"[う] さしすせそ [わをん] たちつてと"
]

パターン2: [?] が()に入っている場合は分割しない
input
"[あ] あいうえお [い] かきくけこ([う] さしす [え] せそ)"
output
[
"[あ] あいうえお",
"[い] かきくけこ([う] さしす [え] せそ)"
]

パターン3: [?]が連続している場合(スペースすらない)はまとめる
input
"[あ] あいうえお [い][う] かきくけこ"
output
[
"[あ] あいうえお",
"[い][う] かきくけこ"
]

理想↓

typescript

1splitByBadge(subject: string): string[] { 2 return subject.split(/* 魔法の正規表現 */); 3}

どんな実装がスマートでしょうか...

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

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

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

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

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

kei344

2017/08/27 06:23

ご自身で書かれたコードを質問文に追記されたほうが回答を得られやすいと思います。
ponsea

2017/08/27 06:47 編集

本当に申し訳ないのですが、今のところこんなのしかできません。。。依存心丸出しですいません。。。 実装出来次第、載せようと思います!
ponsea

2017/08/27 06:50

すみません、この質問、丸投げに該当しますね。。。もうちょっとましな質問にして出直します。。。
m.ts10806

2017/08/28 05:07 編集

既に解決されているので次からで結構なのですが、タイトルの書き方を工夫することであまり自身でコードを組めていなくても丸投げ感が減ります。例えば今回だと「依頼」と入っていることで「丸投げ感」が出てしまうので「依頼」「お願い」といった単語を使わないとか。https://teratail.com/help/question-tips#questionTips3-1
ponsea

2017/08/28 07:14

そうですね。。「依頼」は完全にやってくれって感じがでちゃってますね。内容ももっとアドバイスとか一言求める感じにしとくべきでした。次から気を付けます--;
guest

回答5

0

ベストアンサー

String.prototype.split

String.prototype.split では、パターン1,2までは実装可能です。
[あ], [い] のような境界にマッチさせるには肯定先読みを使う必要があるわけですが、[い][う] に対し、「[い] の手前」にマッチした後で「[う] の手前」を再度テストした場合に [い] が前方に存在する事を感知できない為、パターン3に対応することが出来ません。
いわゆる、「戻り読み」が必要になるというわけです。
また、この書き方は先読みしか使わない為に1文字ずつずらしてテストを繰り返す事となり、パフォーマンス効率が良くないものとなっています(つまり、実行速度が遅いです)。

JavaScript

1'use strict'; 2function sampleSplit (string) { 3 return string.split(/(?=(?:\[[^\]]\]\s*)+(?![^[\]()]*(?:(?:\[[^\]]*\]|\([^)]*\))*[^[\]()]*)*[\])]))/); 4} 5 6console.log(sampleSplit('[あ] あいうえお [い] かきくけこ [う] さしすせそ [わをん] たちつてと')); // ["[あ] あいうえお ", "[い] かきくけこ ", "[う] さしすせそ [わをん] たちつてと"] 7console.log(sampleSplit('[あ] あいうえお [い] かきくけこ([う] さしす [え] せそ)')); // ["[あ] あいうえお ", "[い] かきくけこ ", "[う] さしすせそ [わをん] たちつてと"] 8console.log(sampleSplit('[あ] あいうえお [い][う] かきくけこ')); // ["[あ] あいうえお ", "[い]", "[う] かきくけこ"]

String.prototype.match

String.prototype.match は消費した文字列を配列化する為、正しい文字列パターンを指定すれば同じ場所に2回マッチする事はありません。
後述のネスト(入れ子)された括弧を考慮しなくて良いのであれば、このコードは質問文の要件を満たします。

JavaScript

1'use strict'; 2function sampleMatch (string) { 3 return string.match(/(^(?:\[[^\]]\]\s*)*|(?:\[[^\]]\]\s*)+)[^[\]()]*(?:(?:\[[^\]]{2,}\]|\([^)]*\))*[^[\]()]*)*[^[\]()]*/g); 4} 5 6console.log(sampleMatch('[あ] あいうえお [い] かきくけこ [う] さしすせそ [わをん] たちつてと')); // ["[あ] あいうえお ", "[い] かきくけこ ", "[う] さしすせそ [わをん] たちつてと"] 7console.log(sampleMatch('[あ] あいうえお [い] かきくけこ([う] さしす [え] せそ)')); // ["[あ] あいうえお ", "[い] かきくけこ([う] さしす [え] せそ)"] 8console.log(sampleMatch('[あ] あいうえお [い][う] かきくけこ')); // ["[あ] あいうえお ", "[い][う] かきくけこ"]

ネストされた括弧

今までの書き方では、(), [] がネストされたパターンには対応できません。
ネストされたパターンに対応する為には ( と同じ数だけ ) を消費しなければなりませんが、( の数をカウントして正規表現パターンに反映させる手段が用意されていないからです。
対応する為には RegExp.prototype.exec で繰り返しマッチさせる必要があります。

Re: ponsea さん

投稿2017/08/27 14:12

編集2017/08/27 14:37
think49

総合スコア18162

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

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

ponsea

2017/08/28 01:50

うおお。メソッド1個だけで実現出来ちゃいましたか。。。圧巻の正規表現。恐るべし。 ネストはやはり正規表現だけでは実現不可みたいですね。 解答ありがとうございました!!
think49

2017/08/28 04:27 編集

> ネストはやはり正規表現だけでは実現不可みたいですね。 正確には、正規表現で実装出来ますが、複数回に分けてマッチしなければなりません。 while (eesult = reg.exec(string)) String#replace にコールバック関数を指定すれば、見た目上は一回の処理で終わりますが、無駄に置換処理が働くので、個人的には使用しませんね…(このケースでは)。
guest

0

区切り文字も結果に含めないといけないようですから、splitはそのままでは使えませんね。

こういう作戦はどうでしょう。
0. 「区切り文字」を「元の文字列には絶対に出てこない文字+区切り文字」に置換。
0. パターン2→カッコの中にある「元の文字列には絶対に出てこない文字」を消去。
0. パターン3→「]元の文字列には絶対に出てこない文字]」を「][」に変換。
0. 「元の文字列には絶対に出てこない文字」を区切り文字にしてsplitを実行。

ヒントとして、以下は、「[あ]」・「[い]」・「[う]」のいずれかが出てきたらその直前に"|"をおくコードです。

javascript

1txt1 = "[あ] あいうえお [い] かきくけこ [う] さしすせそ [わをん] たちつてと" 2txt2 = txt1.replace(/(\[[あいう]\])/g,"|$1") 3

投稿2017/08/27 07:09

編集2017/08/27 10:23
KojiDoi

総合スコア13671

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

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

ponsea

2017/08/27 08:44

その方法で試してみます!
guest

0

splitの正規表現では厳しいですか。。。

投稿2017/08/27 09:08

編集2017/08/27 10:03
ponsea

総合スコア15

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

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

think49

2017/08/27 12:38

回答欄で質問しないで下さい。 期待する精度にも依りますが、ざっと頭の中でシミュレーションした限りでは、肯定先読みを使えば不可能ではないように思えますね。
think49

2017/08/27 12:44

すみません、撤回します。 splitではパターン1,2までは対応可能ですが、パターン3の要件を満たす事が出来ませんね。戻り読みがあれば可能ですが。 一回のマッチに拘るなら、String#match ぐらいしか私には思いつきません。
ponsea

2017/08/27 13:23

解答欄に質問、失礼しました。 文字列の分割 => split と安直に考えていましたが、万能ではない見たいですね。 確かに、String#matchからのアプローチもいいかもしれません。 とりあえず今回はreplaceを段階的に使った力業で解決しようと思います。
ponsea

2017/08/27 13:24

コメントありがとうございます
guest

0

KojiDoiさんのアドバイス通りのやり方でやってみました。
これで一応期待通りに動きました!

TypeScript

1function splitByBadge(subject: string): string[] { 2 return subject.replace(/(\[.\])/g, '\\$1') // 区切りの前に印(\)を付ける 3 .replace(/\(.*\\.*\)/g, match => match.replace(/\\/g, '')) // カッコ内にある\は消去して置換 4 .replace(/\]\\\[/g, '][') // 連続している場合はまとめる 5 .split('\\') // \で分割 6 .splice(1); // 先頭の余分な分割を消す 7}

もっと洗練されたやり方や、まずい点があったら教えてください...!!

追記:
誤りがありました。
.replace(/(.\.)/g, match => match.replace(/\/g, ''))
が、"[a] aiueo(aiueo) [b] xxxxx(ddd)"という場合に、[b]の分割を抹消してしまってた.

訂正後:

TypeScript

1function splitByBadge(subject: string): string[] { 2 return subject.replace(/(\[.\])/g, '\\$1') // 区切りの前に印(\)を付ける 3 .replace(/\([^)]*\\[^)]*\)/g, match => match.replace(/\\/g, '')) // カッコ内にある\は消去して置換 4 // ↑変更 5 .replace(/\]\\\[/g, '][') // 連続している場合はまとめる 6 .split('\\') // \で分割 7 .splice(1); // 先頭の余分な分割を消す 8}

もっと洗練されたやり方や、まずい点があったら教えてください...!!

追記:
誤りがありました。
.replace(/([^)]\[^)])/g, match => match.replace(/\/g, ''))
が、"[a] aiueo(aaa(aaa) [b] xyz)"という場合に、[b]を分割してしまう。。。

正規表現苦手だ。。。。

追記:
()の入れ子には対応できませんでしたが、今回は問題なさそうなので、これで行きます。
ありがとうございました。

TypeScript

1function splitByBadge(subject: string): string[] { 2 return subject.replace(/(\[.\])/g, '\\$1') 3 .replace(/\([^)]*\\[^(]*\)/g, match => match.replace(/\\/g, '')) 4 .replace(/\]\\\[/g, '][') 5 .split('\\') 6 .splice(1) 7 .map(s => s.trim()); 8}

投稿2017/08/27 09:07

編集2017/08/27 13:18
ponsea

総合スコア15

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

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

0

パターン 2 が正規表現だけだとつらかったので二段構えで。

typescript

1splitByBadge(subject: string): string[] { 2 return subject.split(/(?=\(.*\))/).reduce((array, str) => { 3 if (/[()]/.test(str)) { 4 array.push((array.pop() || "") + str); 5 return array; 6 } 7 return array.concat(str.split(/(?=[^(\]]\[.\].*)/)); 8 }, []); 9} 10

投稿2017/08/27 08:06

編集2017/08/27 09:37
chitoku

総合スコア1610

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

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

ponsea

2017/08/27 08:44

()内も分割されてしまいました...
chitoku

2017/08/27 09:37 編集

修正してみました
ponsea

2017/08/27 09:55 編集

()内の後の分割してほしいところが分割されませんでした。。 "[a] abc([x] xyz [y] xyz) [b] abcdefg"の[b]の前 書いてないパターンだったの恐縮ですが。。。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問