Почему findstr неправильно обрабатывает регистр (в некоторых случаях)?

При написании некоторых недавних сценариев в cmd.exe у меня возникла необходимость использовать findstr с регулярными выражениями - клиенту требовались стандартные команды cmd.exe (ни GnuWin32, ни Cygwin, ни VBS, ни Powershell).

Я просто хотел знать, содержит ли переменная какие-либо символы верхнего регистра и пытался использовать:

> set myvar=abc
> echo %myvar%|findstr /r "[A-Z]"
abc
> echo %errorlevel%
0

Когда %myvar% установлено в abc, это фактически выводит строку и устанавливает errorlevel в 0, говоря, что совпадение найдено.

Однако вариант полного списка:

> echo %myvar%|findstr /r "[ABCDEFGHIJKLMNOPQRSTUVWXYZ]"
> echo %errorlevel%
1

не выводит строку и правильно устанавливает errorlevel в 1.

Кроме того:

> echo %myvar%|findstr /r "^[A-Z]*$"
> echo %errorlevel%
1

также работает, как ожидалось.

Я явно упускаю что-то здесь, даже если это только тот факт, что findstr каким-то образом сломан.

Почему первое регулярное выражение (диапазон) не работает в этом случае?


И еще странности:

> echo %myvar%|findstr /r "[A-Z]"
abc
> echo %myvar%|findstr /r "[A-Z][A-Z]"
abc
> echo %myvar%|findstr /r "[A-Z][A-Z][A-Z]"
> echo %myvar%|findstr /r "[A]"

Последние два выше также не выводят строку!!


person paxdiablo    schedule 14.04.2010    source источник
comment
хорошо, справка findstr показывает, что /I switch устанавливает режим без учета регистра, но я не могу заставить findstr учитывать регистр независимо от того, что я делаю, используя диапазон!   -  person Axarydax    schedule 14.04.2010
comment
после моего первоначального фальстарта я могу только вторым (или третьим) обоим наблюдениям. Вы уже указали обходной путь для того, что кажется ошибкой в ​​​​findstr... используйте вариант с полным списком.   -  person Lieven Keersmaekers    schedule 14.04.2010
comment
просто примечание: echo %myvar%|findstr /r "^[A-Z]*$" на самом деле не работает, после abc есть пробел, если вы измените "^[A-Z]*$" на "^[A-Z]* $", он все равно будет выводить abc как в [A-Z], а уровень ошибки равен 0   -  person YOU    schedule 14.04.2010
comment
На самом деле это может быть еще одна ошибка, @S.Mark, шаблон "^ [A-Z]*$ (с большим количеством начальных пробелов) дает те же результаты, и, если вы записываете вывод в файл, пробелов нет.   -  person paxdiablo    schedule 14.04.2010
comment
Я только что столкнулся с этой проблемой; спасибо за решение (обходной путь). Вот еще немного дополнительной информации: [a-z] также находит символы с диакритическими знаками (в файле с кодировкой ANSI, а не в Unicode). Так что похоже, что эта ошибка может возникнуть из-за того, что код пытается поддерживать декорированные символы.   -  person Rhubbarb    schedule 01.05.2011
comment
Кроме того, [az], похоже, не захватывает прописную букву «Z», а [AZ], похоже, не захватывает строчную «a».   -  person Rhubbarb    schedule 01.05.2011
comment
@YOU - нет места ни до, ни после abc. Строка "^[A-Z] *" соответствует, потому что она разбита на 2 поиска регулярных выражений. То же самое верно и для примера paxdiablo с "^ [A-Z]*$". Если вам нужно одно регулярное выражение, вам нужно /c:"^[A-Z] $", что не соответствует. Единственное регулярное выражение "^[aA-Z]*$" действительно соответствует, что ожидается на основе текущего понимания диапазонов классов char.   -  person dbenham    schedule 24.01.2012


Ответы (4)


Я считаю, что это в основном ужасный недостаток дизайна.

Мы все ожидаем, что диапазоны будут сопоставляться на основе значения кода ASCII. Но это не так — вместо этого диапазоны основаны на последовательности сопоставления, которая почти соответствует последовательности по умолчанию, используемой SORT. EDIT – Точная последовательность сопоставления, используемая FINDSTR, теперь доступна по адресу https://stackoverflow.com/a/20159191/1012053 в разделе Диапазоны классов символов Regex [xy].

Я подготовил текстовый файл, содержащий по одной строке для каждого расширенного символа ASCII от 1 до 255, за исключением 10 (LF), 13 (CR) и 26 (EOF в Windows). В каждой строке у меня есть символ, за которым следует пробел, за которым следует десятичный код символа. Затем я прогнал файл через SORT и зафиксировал результат в файле sortedChars.txt.

Теперь я могу легко проверить любой диапазон регулярных выражений на этом отсортированном файле и продемонстрировать, как диапазон определяется последовательностью сопоставления, которая почти такая же, как SORT.

>findstr /nrc:"^[0-9]" sortedChars.txt
137:0 048
138:½ 171
139:¼ 172
140:1 049
141:2 050
142:² 253
143:3 051
144:4 052
145:5 053
146:6 054
147:7 055
148:8 056
149:9 057

Результаты не совсем такие, как мы ожидали, потому что в смесь добавлены символы 171, 172 и 253. Но результаты имеют смысл. Префикс номера строки соответствует последовательности сортировки SORT, и вы можете видеть, что диапазон точно соответствует последовательности SORT.

Вот еще один тест диапазона, который точно следует последовательности SORT:

>findstr /nrc:"^[!-=]" sortedChars.txt
34:! 033
35:" 034
36:# 035
37:$ 036
38:% 037
39:& 038
40:( 040
41:) 041
42:* 042
43:, 044
44:. 046
45:/ 047
46:: 058
47:; 059
48:? 063
49:@ 064
50:[ 091
51:\ 092
52:] 093
53:^ 094
54:_ 095
55:` 096
56:{ 123
57:| 124
58:} 125
59:~ 126
60:¡ 173
61:¿ 168
62:¢ 155
63:£ 156
64:¥ 157
65:₧ 158
66:+ 043
67:∙ 249
68:< 060
69:= 061

Есть одна небольшая аномалия с альфа-символами. Символ "a" сортируется между "A" и "Z", но не соответствует [A-Z]. "z" сортируется после "Z", но соответствует [A-Z]. Существует соответствующая проблема с [a-z]. "A" сортируется перед "a", но соответствует [a-z]. "Z" сортирует между "a" и "z", но не соответствует [a-z].

Вот результаты [A-Z]:

>findstr /nrc:"^[A-Z]" sortedChars.txt
151:A 065
153:â 131
154:ä 132
155:à 133
156:å 134
157:Ä 142
158:Å 143
159:á 160
160:ª 166
161:æ 145
162:Æ 146
163:B 066
164:b 098
165:C 067
166:c 099
167:Ç 128
168:ç 135
169:D 068
170:d 100
171:E 069
172:e 101
173:é 130
174:ê 136
175:ë 137
176:è 138
177:É 144
178:F 070
179:f 102
180:ƒ 159
181:G 071
182:g 103
183:H 072
184:h 104
185:I 073
186:i 105
187:ï 139
188:î 140
189:ì 141
190:í 161
191:J 074
192:j 106
193:K 075
194:k 107
195:L 076
196:l 108
197:M 077
198:m 109
199:N 078
200:n 110
201:ñ 164
202:Ñ 165
203:ⁿ 252
204:O 079
205:o 111
206:ô 147
207:ö 148
208:ò 149
209:Ö 153
210:ó 162
211:º 167
212:P 080
213:p 112
214:Q 081
215:q 113
216:R 082
217:r 114
218:S 083
219:s 115
220:ß 225
221:T 084
222:t 116
223:U 085
224:u 117
225:û 150
226:ù 151
227:ú 163
228:ü 129
229:Ü 154
230:V 086
231:v 118
232:W 087
233:w 119
234:X 088
235:x 120
236:Y 089
237:y 121
238:ÿ 152
239:Z 090
240:z 122

И результаты [a-z]

>findstr /nrc:"^[a-z]" sortedChars.txt
151:A 065
152:a 097
153:â 131
154:ä 132
155:à 133
156:å 134
157:Ä 142
158:Å 143
159:á 160
160:ª 166
161:æ 145
162:Æ 146
163:B 066
164:b 098
165:C 067
166:c 099
167:Ç 128
168:ç 135
169:D 068
170:d 100
171:E 069
172:e 101
173:é 130
174:ê 136
175:ë 137
176:è 138
177:É 144
178:F 070
179:f 102
180:ƒ 159
181:G 071
182:g 103
183:H 072
184:h 104
185:I 073
186:i 105
187:ï 139
188:î 140
189:ì 141
190:í 161
191:J 074
192:j 106
193:K 075
194:k 107
195:L 076
196:l 108
197:M 077
198:m 109
199:N 078
200:n 110
201:ñ 164
202:Ñ 165
203:ⁿ 252
204:O 079
205:o 111
206:ô 147
207:ö 148
208:ò 149
209:Ö 153
210:ó 162
211:º 167
212:P 080
213:p 112
214:Q 081
215:q 113
216:R 082
217:r 114
218:S 083
219:s 115
220:ß 225
221:T 084
222:t 116
223:U 085
224:u 117
225:û 150
226:ù 151
227:ú 163
228:ü 129
229:Ü 154
230:V 086
231:v 118
232:W 087
233:w 119
234:X 088
235:x 120
236:Y 089
237:y 121
238:ÿ 152
240:z 122

Сортировка сортирует прописные буквы перед строчными. (EDIT - я только что прочитал справку по SORT и узнал, что он не делает различий между прописными и строчными буквами. Тот факт, что мой вывод SORT последовательно помещал верхний регистр перед нижним, вероятно, является результатом порядка ввода.)< /em> Но регулярное выражение, по-видимому, сортирует нижний регистр перед прописным. Все следующие диапазоны не соответствуют ни одному символу.

>findstr /nrc:"^[A-a]" sortedChars.txt

>findstr /nrc:"^[B-b]" sortedChars.txt

>findstr /nrc:"^[C-c]" sortedChars.txt

>findstr /nrc:"^[D-d]" sortedChars.txt

Обратный порядок находит символы.

>findstr /nrc:"^[a-A]" sortedChars.txt
151:A 065
152:a 097

>findstr /nrc:"^[b-B]" sortedChars.txt
163:B 066
164:b 098

>findstr /nrc:"^[c-C]" sortedChars.txt
165:C 067
166:c 099

>findstr /nrc:"^[d-D]" sortedChars.txt
169:D 068
170:d 100

Есть дополнительные символы, которые регулярное выражение сортирует иначе, чем SORT, но у меня нет точного списка.

person dbenham    schedule 07.01.2012
comment
Поскольку сообщество теперь ценит ваш ответ выше, чем мой (и, скажем прямо, это лучший ответ с более подробным объяснением того, почему это происходит), я решил изменить принятый ответ на этот. - person paxdiablo; 14.01.2012

Итак, если вы хотите

  • только цифры : FindStr /R "^[0123-9]*$"

  • восьмеричное: FindStr /R "^[0123-7]*$"

  • шестнадцатеричный : FindStr /R "^[0123-9aAb-Cd-EfF]*$"

  • альфа без акцента : FindStr /R "^[aAb-Cd-EfFg-Ij-NoOp-St-Uv-YzZ]*$"

  • буквенно-цифровой : FindStr /R "^[0123-9aAb-Cd-EfFg-Ij-NoOp-St-Uv-YzZ]*$"

person JLGautier    schedule 10.12.2015

По-видимому, это вызвано использованием диапазонов при поиске по регулярным выражениям.

Это не происходит для первого символа в диапазоне. Это вообще не происходит для не-диапазонов.

> echo a | findstr /r "[A-C]"
> echo b | findstr /r "[A-C]"
    b
> echo c | findstr /r "[A-C]"
    c
> echo d | findstr /r "[A-C]"
> echo b | findstr /r "[B-C]"
> echo c | findstr /r "[B-C]"
    c

> echo a | findstr /r "[ABC]"
> echo b | findstr /r "[ABC]"
> echo c | findstr /r "[ABC]"
> echo d | findstr /r "[ABC]"
> echo b | findstr /r "[BC]"
> echo c | findstr /r "[BC]"

> echo A | findstr /r "[A-C]"
    A
> echo B | findstr /r "[A-C]"
    B
> echo C | findstr /r "[A-C]"
    C
> echo D | findstr /r "[A-C]"

Согласно SS64 CMD FINDSTR page (на которой, в ошеломляющем отображении цикличности, содержится ссылка на этот вопрос), диапазон [A-Z]:

... включает полный английский алфавит, как верхний, так и нижний регистр (кроме «а»), а также неанглийские буквенные символы с диакритическими знаками.

Чтобы обойти проблему в моей среде, я просто использовал определенные регулярные выражения (например, [ABCD], а не [A-D]). Более разумным подходом для тех, кому разрешено, будет загрузка CygWin или GnuWin32 и используйте grep из одного из этих пакетов.

person paxdiablo    schedule 11.06.2010

Все выше не правы. Порядок альфа-символов следующий: aAbBcCdDeE..zZ, поэтому echo a | findstr /r "[A-Z]" ничего не возвращает, так как a находится за пределами этого диапазона.

echo abc|findstr /r "[A-Z][A-Z][A-Z]" также ничего не возвращает, поскольку первая группа диапазонов соответствует b, вторая соответствует c, а третья ничего не соответствует, и, таким образом, весь шаблон регулярного выражения ничего не находит.

Если вы хотите сопоставить любой символ латинского алфавита - используйте [a-Z].

person no kudos    schedule 15.07.2015
comment
Это ничем не отличается от того, что я сказал в своем ответе: Но регулярное выражение, по-видимому, сортирует нижний регистр перед прописным.. Во время моего расследования я ошибочно подумал, что команда SORT сортирует верхнее, а затем нижнее, но позже я признаю, что это была ошибка - SORT не различает регистр, что совершенно не связано с последовательностью сопоставления регулярных выражений. Точная последовательность сопоставления регулярных выражений доступна по адресу stackoverflow.com/a/8844873/1012053. - person dbenham; 12.10.2015