質問するログイン新規登録

回答編集履歴

2

「先読みは文字を消費しない」を追記

2020/04/29 11:26

投稿

think49
think49

スコア18194

answer CHANGED
@@ -52,4 +52,34 @@
52
52
  console.log('abc100000000def'.replace(/(\d)(?=(?:\d{3})+(?!\d))/g, '$1,')); // "abc100,000,000def"
53
53
  ```
54
54
 
55
+ ### 先読みは文字を消費しない
56
+
57
+ 「先読み」の正規表現を説明する時、
58
+
59
+ - 位置にマッチする
60
+ - 文字と文字の間にマッチする
61
+
62
+ という表現が良く使われます。
63
+ 手元の「詳説 正規表現 第三版」でも次の一文がありました。
64
+
65
+ > ##### 2.3.5 先後読みによって数値にカンマを付け加える
66
+ > ...
67
+ > 先後読み構文は、テキストではなく、テキストの中の**位置**にマッチするという点で、「\b」などの単語境界メタ文字、「^」や「$」などのアンカーに似ている。
68
+
69
+ これはこれで納得できる説明ですが、次節の
70
+
71
+ > ##### テキストを "消費" しない先後読み
72
+
73
+ 「消費」の概念が本質なのかもしれません。
74
+ 正規表現を実装する立場で考えた場合、「`^`」を「**1文字も消費していない状態**」と定義する事が出来ます。
75
+ その考えで正規表現を読み解くと、
76
+
77
+ ```JavaScript
78
+ /(?!^)/
79
+ ```
80
+
81
+ 上記正規表現は、「**1文字も消費していない状態にはマッチせず、この検索で文字列を消費しない**」という事になります。
82
+
83
+ ※「先端を否定先読みする」という正規表現は先端(^)の手前があるかのようで不思議に感じられましたが、「消費」の概念を人間に分かりやすく説明する為に「位置」や「文字と文字の間」という説明が生まれた、と考えると、この挙動の辻褄が合うように思います。
84
+
55
85
  Re: takahashi-one さん

1

(?!^)、別解を追記

2020/04/29 11:26

投稿

think49
think49

スコア18194

answer CHANGED
@@ -15,4 +15,41 @@
15
15
 
16
16
  `/(?=,)/` は**カンマ(,)が後続する文字間の境界にマッチ**しています。
17
17
 
18
+ ### (?!^)
19
+
20
+ > (?!^)の対象は何になるのでしょうか?
21
+
22
+ まず、`(?!^)` は文字列の先端にカンマが挿入されないようにする為のものです。
23
+
24
+ ```JavaScript
25
+ console.log('100000000'.replace(/(?=(?:\d{3})+$)/g, ',')); // ",100,000,000"
26
+ console.log('100000000'.replace(/(?!^)(?=(?:\d{3})+$)/g, ',')); // "100,000,000"
27
+ ```
28
+
29
+ `^` は「文字列の先端」を表し、1文字目の一つ手間の境界にマッチします。
30
+ これを先読みしますので、「1文字目の一つ手間の境界(`^`)」から更に一つ手前の境界にマッチしているのでしょう。
31
+
32
+ 下記理由でお勧めし難い正規表現ではありますが…。
33
+
34
+ - 境界の手前に境界があるのは非論理的に感じます
35
+ - ECMAScript 仕様は未確認です
36
+ - 複数ブラウザで期待通りに動くか分かりません
37
+
38
+ ### 別解
39
+
40
+ 後読みを使うと、部分一致で置換する正規表現を実装できるようになります。
41
+
42
+ ```JavaScript
43
+ console.log('100000000'.replace(/(?<=\d)(?=(?:\d{3})+(?!\d))/g, ',')); // "100,000,000"
44
+ console.log('abc100000000def'.replace(/(?<=\d)(?=(?:\d{3})+(?!\d))/g, ',')); // "abc100,000,000def"
45
+ ```
46
+
47
+ こちらは、`(?<=\d)` によって数字が前述することを保証するので、`^` にまつわる不自然さは解消されています。
48
+ 変数にキャプチャすることをいとわなければ、先読みだけでも同じことが可能です。
49
+
50
+ ```JavaScript
51
+ console.log('100000000'.replace(/(\d)(?=(?:\d{3})+(?!\d))/g, '$1,')); // "100,000,000"
52
+ console.log('abc100000000def'.replace(/(\d)(?=(?:\d{3})+(?!\d))/g, '$1,')); // "abc100,000,000def"
53
+ ```
54
+
18
55
  Re: takahashi-one さん