2010-04-14 26 views
14

Al escribir algunas secuencias de comandos recientes en cmd.exe, tuve la necesidad de usar findstr con expresiones regulares - los comandos estándar de cmd.exe requeridos por el cliente (no GnuWin32 ni Cygwin ni VBS ni Powershell).¿Por qué findstr no maneja el estuche correctamente (en algunas circunstancias)?

Sólo quería saber si una variable contiene los caracteres en mayúscula y trató de utilizar:

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

Cuando %myvar% se establece en abc, que realmente muestra la cadena y establece errorlevel a 0, diciendo que se encontró una coincidencia.

Sin embargo, la lista completa variante:

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

hace no salida de la línea y se establecen correctamente errorlevel a 1.

Además:

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

funciona también como se esperaba.

Obviamente me falta algo aquí, incluso si es sólo el hecho de que findstr se ha roto de alguna manera.

¿Por qué la primera (rango) expresión regular no funciona en este caso?


Y sin embargo, más rarezas:

> 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]" 

Las dos últimas anterior también no da salida a la cadena !!

+0

bien de findstr muestra que/I Mode Sets entre mayúsculas y minúsculas, pero no puede llegar findstr ser entre mayúsculas y minúsculas, no importa lo que hago uso de la gama, también! – Axarydax

+0

después de mi inicio falso inicial, solo puedo realizar un segundo (o es el tercero) ambas observaciones. Ya ha dado la solución para lo que parece ser un error en findstr ... use la variante de lista completa. –

+0

solo una nota: 'echo% myvar% | findstr/r"^[AZ] * $ "' en realidad no funciona, hay espacios en blanco después de 'abc', si cambia '"^[AZ] * $ "' a '"^[AZ] * $ "', seguirá generando 'abc' como en" [AZ] ", y el nivel de error es 0 – YOU

Respuesta

14

Creo que esto es principalmente un error de diseño horrible.

Todos esperamos que los intervalos se cotejen en función del valor del código ASCII. Pero no lo hacen; en cambio, los rangos se basan en una secuencia de intercalación que casi coincide con la secuencia predeterminada utilizada por SORT.EDITAR-La secuencia de intercalación exacto utilizado por FINDSTR ya está disponible en https://stackoverflow.com/a/20159191/1012053 bajo la sección titulada rangos de clase de caracteres Regex [X-Y].

Preparé un archivo de texto que contiene una línea para cada carácter ASCII extendido del 1 al 255, excluyendo 10 (LF), 13 (CR) y 26 (EOF en Windows). En cada línea, tengo el carácter, seguido de un espacio, seguido del código decimal para el personaje. Luego ejecuté el archivo a través de SORT y capturé el resultado en un archivo sortedChars.txt.

Ahora puedo probar fácilmente cualquier rango de expresiones regex contra este archivo ordenado y demostrar cómo el rango está determinado por una secuencia de intercalación que es casi igual a 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 

Los resultados no son exactamente lo que esperábamos en ese tipo char de 171, 172 y 253 son lanzados en la mezcla. Pero los resultados tienen perfecto sentido. El prefijo del número de línea corresponde a la secuencia de clasificación SORT, y puede ver que el rango coincide exactamente de acuerdo con la secuencia SORT.

Aquí es otra prueba de rango que sigue exactamente la secuencia de clasificación:

>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 

Hay una pequeña anomalía con caracteres alfabéticos. El carácter "a" ordena entre "A" y "Z" pero no coincide con [A-Z]. "z" ordena después de "Z", pero coincide con [A-Z]. Hay un problema correspondiente con [a-z]. "A" ordena antes que "a", pero coincide con [a-z]. "Z" ordena entre "a" y "z", pero no coincide con [a-z].

Éstos son los [A-Z] resultados:

>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 

Y el [a-z] Resultados

>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 

Ordenar ordena mayúsculas antes de minúsculas. (EDITAR - Acabo de leer la ayuda para SORT y me di cuenta de que no distingue entre mayúsculas y minúsculas. El hecho de que mi salida SORT siempre coloque superior antes que bajo es probablemente el resultado del orden de la entrada.) Pero regex aparentemente ordena minúsculas antes de mayúsculas. Todos los siguientes rangos no coinciden con ningún carácter.

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

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

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

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

Al invertir la orden se encuentran los caracteres.

>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 

Hay caracteres adicionales que la expresión regular ordena de forma diferente a SORT, pero no tengo una lista precisa.

+0

Dado que la comunidad ahora valora su respuesta más que la mía (y, seamos sinceros, es una respuesta mejor también con más explicación de por qué sucede esto), he decidido cambiar la respuesta aceptada a esta. – paxdiablo

2

Esto parece deberse al uso de rangos dentro de las búsquedas de expresiones regulares.

No ocurre para el primer caracter en el rango. No ocurre en absoluto para los no rangos.

> 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]" 

De acuerdo con la SS64 CMD FINDSTR page (que, en un impresionante despliegue de la circularidad, las referencias a esta pregunta), el rango [A-Z]:

... incluye el alfabeto completo Inglés, tanto en mayúsculas y minúsculas (a excepción de "a"), así como caracteres alfa no ingleses con signos diacríticos.

Para evitar el problema en mi entorno, simplemente utilicé expresiones regulares específicas (como [ABCD] en lugar de [A-D]). Un enfoque más sensato para aquellos que están permitidos sería descargar CygWin o GnuWin32 y usar grep desde uno de esos paquetes.

-1

Todos los anteriores están equivocados. El orden alfa de caracteres es el siguiente: aAbBcCdDeE..zZ por lo que echo a | findstr /r "[A-Z]" no devuelve nada, ya que a está fuera de ese rango.

echo abc|findstr /r "[A-Z][A-Z][A-Z]" tampoco devuelve nada, ya que el primer grupo de rangos coincide con b, el segundo coincide con c y el tercero no coincide con nada, por lo que el patrón de expresiones regulares no encuentra nada.

Si desea hacer coincidir cualquier caracter del alfabeto latino - use [a-Z].

+0

Esto no es diferente de lo que dije en mi respuesta: * "Pero regex aparentemente clasifica minúsculas antes de mayúsculas". *. Durante mi investigación, creí erróneamente que el comando SORT ordenaba upper before lower, pero más tarde reconozco que fue un error: SORT no diferencia entre case, que no está relacionado con la secuencia de intercalación de expresiones regulares. La secuencia exacta de clasificación de expresiones regulares está disponible en http://stackoverflow.com/a/8844873/1012053 – dbenham

3

Así que si quieres

  • sólo números: FindStr /R "^[0123-9]*$"

  • octal: FindStr /R "^[0123-7]*$"

  • hexadécimal: FindStr /R "^[0123-9aAb-Cd-EfF]*$"

  • alfa sin acento: FindStr /R "^[aAb-Cd-EfFg-Ij-NoOp-St-Uv-YzZ]*$"

  • alfanumérica: ayuda FindStr /R "^[0123-9aAb-Cd-EfFg-Ij-NoOp-St-Uv-YzZ]*$"

Cuestiones relacionadas