回答編集履歴

2

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

2020/04/29 11:26

投稿

think49
think49

スコア18189

test CHANGED
@@ -106,4 +106,64 @@
106
106
 
107
107
 
108
108
 
109
+ ### 先読みは文字を消費しない
110
+
111
+
112
+
113
+ 「先読み」の正規表現を説明する時、
114
+
115
+
116
+
117
+ - 位置にマッチする
118
+
119
+ - 文字と文字の間にマッチする
120
+
121
+
122
+
123
+ という表現が良く使われます。
124
+
125
+ 手元の「詳説 正規表現 第三版」でも次の一文がありました。
126
+
127
+
128
+
129
+ > ##### 2.3.5 先後読みによって数値にカンマを付け加える
130
+
131
+ > ...
132
+
133
+ > 先後読み構文は、テキストではなく、テキストの中の**位置**にマッチするという点で、「\b」などの単語境界メタ文字、「^」や「$」などのアンカーに似ている。
134
+
135
+
136
+
137
+ これはこれで納得できる説明ですが、次節の
138
+
139
+
140
+
141
+ > ##### テキストを "消費" しない先後読み
142
+
143
+
144
+
145
+ 「消費」の概念が本質なのかもしれません。
146
+
147
+ 正規表現を実装する立場で考えた場合、「`^`」を「**1文字も消費していない状態**」と定義する事が出来ます。
148
+
149
+ その考えで正規表現を読み解くと、
150
+
151
+
152
+
153
+ ```JavaScript
154
+
155
+ /(?!^)/
156
+
157
+ ```
158
+
159
+
160
+
161
+ 上記正規表現は、「**1文字も消費していない状態にはマッチせず、この検索で文字列を消費しない**」という事になります。
162
+
163
+
164
+
165
+ ※「先端を否定先読みする」という正規表現は先端(^)の手前があるかのようで不思議に感じられましたが、「消費」の概念を人間に分かりやすく説明する為に「位置」や「文字と文字の間」という説明が生まれた、と考えると、この挙動の辻褄が合うように思います。
166
+
167
+
168
+
109
169
  Re: takahashi-one さん

1

(?!^)、別解を追記

2020/04/29 11:26

投稿

think49
think49

スコア18189

test CHANGED
@@ -32,4 +32,78 @@
32
32
 
33
33
 
34
34
 
35
+ ### (?!^)
36
+
37
+
38
+
39
+ > (?!^)の対象は何になるのでしょうか?
40
+
41
+
42
+
43
+ まず、`(?!^)` は文字列の先端にカンマが挿入されないようにする為のものです。
44
+
45
+
46
+
47
+ ```JavaScript
48
+
49
+ console.log('100000000'.replace(/(?=(?:\d{3})+$)/g, ',')); // ",100,000,000"
50
+
51
+ console.log('100000000'.replace(/(?!^)(?=(?:\d{3})+$)/g, ',')); // "100,000,000"
52
+
53
+ ```
54
+
55
+
56
+
57
+ `^` は「文字列の先端」を表し、1文字目の一つ手間の境界にマッチします。
58
+
59
+ これを先読みしますので、「1文字目の一つ手間の境界(`^`)」から更に一つ手前の境界にマッチしているのでしょう。
60
+
61
+
62
+
63
+ 下記理由でお勧めし難い正規表現ではありますが…。
64
+
65
+
66
+
67
+ - 境界の手前に境界があるのは非論理的に感じます
68
+
69
+ - ECMAScript 仕様は未確認です
70
+
71
+ - 複数ブラウザで期待通りに動くか分かりません
72
+
73
+
74
+
75
+ ### 別解
76
+
77
+
78
+
79
+ 後読みを使うと、部分一致で置換する正規表現を実装できるようになります。
80
+
81
+
82
+
83
+ ```JavaScript
84
+
85
+ console.log('100000000'.replace(/(?<=\d)(?=(?:\d{3})+(?!\d))/g, ',')); // "100,000,000"
86
+
87
+ console.log('abc100000000def'.replace(/(?<=\d)(?=(?:\d{3})+(?!\d))/g, ',')); // "abc100,000,000def"
88
+
89
+ ```
90
+
91
+
92
+
93
+ こちらは、`(?<=\d)` によって数字が前述することを保証するので、`^` にまつわる不自然さは解消されています。
94
+
95
+ 変数にキャプチャすることをいとわなければ、先読みだけでも同じことが可能です。
96
+
97
+
98
+
99
+ ```JavaScript
100
+
101
+ console.log('100000000'.replace(/(\d)(?=(?:\d{3})+(?!\d))/g, '$1,')); // "100,000,000"
102
+
103
+ console.log('abc100000000def'.replace(/(\d)(?=(?:\d{3})+(?!\d))/g, '$1,')); // "abc100,000,000def"
104
+
105
+ ```
106
+
107
+
108
+
35
109
  Re: takahashi-one さん