回答編集履歴

1

無限ループ問題への対処コード追記

2017/01/12 03:44

投稿

think49
think49

スコア18162

test CHANGED
@@ -1,3 +1,7 @@
1
+ ### String.prototype.indexOf
2
+
3
+
4
+
1
5
  問題なく動きましたので、現象を再現可能なコードを開示する事をお勧めします。
2
6
 
3
7
 
@@ -12,4 +16,254 @@
12
16
 
13
17
 
14
18
 
19
+ ### 残存する問題点
20
+
21
+
22
+
23
+ 下記コードは質問文のコードをそのまま使用したものです。
24
+
25
+
26
+
27
+ - [String#indexOfで文字列を抽出 - JSFiddle](https://jsfiddle.net/0vtxod1e/)
28
+
29
+
30
+
31
+ 質問の本題部分は ikedas さんの回答で解決できていますが、まだ問題が残っているように思います。
32
+
33
+
34
+
35
+ **ローカル変数 lines**
36
+
37
+
38
+
39
+ > ```JavaScript
40
+
41
+ > var lines = []; // 抜き出した文字列
42
+
43
+ > ```
44
+
45
+
46
+
47
+ 区切り文字が改行であるとは限らない為、変数名 `lines` に違和感があります。
48
+
49
+ 質問文のケースにあてはめても改行を含まない文字列に対して処理を行う事が可能です。
50
+
51
+
52
+
53
+ ```JavaScript
54
+
55
+ console.log(createLinesCount('「あ」「漢」「alpha」', '「', '」')); // ["「あ」","「漢」","「alpha」"]
56
+
57
+ ```
58
+
59
+
60
+
61
+ **第三引数(検索終了文字)の省略**
62
+
63
+
64
+
65
+ > ```JavaScript
66
+
67
+ > // 検索終了文字が指定されていなければ、改行コードを終了文字にする
68
+
69
+ > if ( to_text == "" ) to_text = "\n";
70
+
71
+ > ```
72
+
73
+
74
+
75
+ `to_text` が指定されなかった場合、`undefined` として評価されますが、`undefined == ""` は「偽」なのでこの条件式は正しく機能しません。
76
+
77
+ 従って、第三引数を省略した場合に `to_text.length` の部分で「TypeError 例外」が発生してしまいます。
78
+
79
+
80
+
81
+ ```JavaScript
82
+
83
+ createLinesCount('「あ」\n「漢」\n「よ」\n「alpha」', '「'); // TypeError: Cannot read property 'length' of undefined
84
+
85
+ ```
86
+
87
+
88
+
89
+ **検索終了文字列が持つからないと無限ループする**
90
+
91
+
92
+
93
+ > ```JavaScript
94
+
95
+ > // 検索終了位置を取得
96
+
97
+ > end = start + from_text.length;
98
+
99
+ > end = text.indexOf(to_text, end) + to_text.length;
100
+
101
+ > ```
102
+
103
+
104
+
105
+ 検索終了文字列('to_text')が見つからなかった時の配慮がありません。
106
+
107
+ 仮に次のコードを実行した場合、
108
+
109
+
110
+
111
+ ```JavaScript
112
+
113
+ createLinesCount('「あ\n「漢\n「よ\n「alpha', '「', '」')); // "」" が見つからないので無限ループしてしまう
114
+
115
+ ```
116
+
117
+
118
+
119
+ 検索終了文字列('"」"')が見つからない為に `text.indexOf(to_text, end)` の評価値が -1 となり、to_text.length は 1 なので end が `0` にリセットされます。
120
+
121
+ すると、初めから検索をやり直すことになるので **while 文が無限ループ**してしまいます。
122
+
123
+
124
+
125
+ - [String#indexOfで文字列を抽出 (ikedasさんの修正適用版) - JSFiddle](https://jsfiddle.net/0vtxod1e/1/) (←は無限ループします)
126
+
127
+
128
+
129
+ **while(1)**
130
+
131
+
132
+
133
+ > ```JavaScript
134
+
135
+ > while( 1 ){
136
+
137
+ > ```
138
+
139
+
140
+
141
+ `while (1)` は無限ループになるリスクがある為、可能な限りループ継続条件となる条件式を与えてやるのが好ましいと思います。
142
+
143
+ 前述の無限ループもwhile条件を見直すことで回避する事が可能です。
144
+
145
+
146
+
147
+ ### 修正コード (String#indexOf版)
148
+
149
+
150
+
151
+ 上記問題を修正したコードがこちら。
152
+
153
+
154
+
155
+ - [String#indexOfで文字列を抽出 (無限ループ不具合の修正版) - JSFiddle](https://jsfiddle.net/0vtxod1e/2/)
156
+
157
+
158
+
159
+ ```JavaScript
160
+
161
+ 'use strict';
162
+
163
+ function extractFromString(targetString, startString, endString){ // 検索対象文字列, 検索開始文字列, 検索終了文字列
164
+
165
+ // 全ての引数を String 型に変換する
166
+
167
+ targetString = String(targetString);
168
+
169
+ startString = String(startString);
170
+
171
+ endString = arguments.length > 2 ? String(endString) : '\n'; // 検索終了文字が指定されていなければ、改行コードを終了文字にする
172
+
173
+
174
+
175
+ var startStringLength = startString.length, // 検索開始文字列の文字数
176
+
177
+ endStringLength = endString.length, // 検索終了文字列の文字数
178
+
179
+ extractedStrings = [], // 抜き出した文字列リスト
180
+
181
+ startPosition,
182
+
183
+ endPosition;
184
+
185
+
186
+
187
+ do {
188
+
189
+ startPosition = targetString.indexOf(startString, startPosition);
190
+
191
+
192
+
193
+ if (startPosition !== -1) {
194
+
195
+ endPosition = targetString.indexOf(endString, startPosition + startStringLength);
196
+
197
+
198
+
199
+ if (endPosition !== -1) {
200
+
201
+ extractedStrings.push(targetString.slice(startPosition, endPosition + 1));
202
+
203
+ startPosition = endPosition + endStringLength;
204
+
205
+ }
206
+
207
+ }
208
+
209
+ } while (startPosition !== -1 && endPosition !== -1);
210
+
211
+
212
+
213
+ return extractedStrings;
214
+
215
+ }
216
+
217
+
218
+
219
+ console.log(extractFromString('「あ」\n「漢」\n「よ」\n「alpha」', '「', '」')); // ["「あ」", "「漢」", "「よ」", "「alpha」"]
220
+
221
+ console.log(extractFromString('「あ」「漢」「よ」「alpha」', '「', '」')); // ["「あ」", "「漢」", "「よ」", "「alpha」"]
222
+
223
+ console.log(extractFromString('「あ」\n「漢」\n「よ」\n「alpha」', '「')); // ["「あ」\n", "「漢」\n", "「よ」\n"]
224
+
225
+ ```
226
+
227
+
228
+
229
+ ### 修正コード (正規表現版)
230
+
231
+
232
+
233
+ 正規表現を利用するとスマートに実装できます。
234
+
235
+
236
+
237
+ - [String#indexOfで文字列を抽出 (正規表現版) - JSFiddle](https://jsfiddle.net/0vtxod1e/3/)
238
+
239
+
240
+
241
+ ```JavaScript
242
+
243
+ function extractFromString2 (targetString, startString, endString) {
244
+
245
+ // 正規表現化する為に文字列をエスケープする
246
+
247
+ startString = String(startString).replace(/(?=[$()*+\-.?\[]^{|}])/g, '\\');
248
+
249
+ endString = arguments.length > 2 ? String(endString).replace(/(?=[$()*+\-.?\[]^{|}])/g, '\\') : '\n';
250
+
251
+
252
+
253
+ return String(targetString).match(new RegExp(startString + '[^' + endString + ']*' + endString, 'g'));
254
+
255
+ }
256
+
257
+
258
+
259
+ console.log(extractFromString2('「あ」\n「漢」\n「よ」\n「alpha」', '「', '」')); // ["「あ」", "「漢」", "「よ」", "「alpha」"]
260
+
261
+ console.log(extractFromString2('「あ」「漢」「よ」「alpha」', '「', '」')); // ["「あ」", "「漢」", "「よ」", "「alpha」"]
262
+
263
+ console.log(extractFromString2('「あ」\n「漢」\n「よ」\n「alpha」', '「')); // ["「あ」\n", "「漢」\n", "「よ」\n"]
264
+
265
+ ```
266
+
267
+
268
+
15
269
  Re: nnahito さん