Hacer coincidir espacios en blanco pero no líneas nuevas

277

A veces quiero hacer coincidir espacios en blanco pero no nueva línea.

Hasta ahora he estado recurriendo [ \t]. ¿Hay alguna manera menos incómoda?

JoelFan
fuente
44
Por cierto, estos personajes son también "espacios en blanco": [\r\f].
Eugene Yarmash
2
@eugeney, ¿alguien sigue haciendo feeds de formularios? (\ f's)
Aran Mulholland
1
@AranMulholland: Cualquiera que tenga una impresora orientada a los personajes. La mayoría de las impresoras tienen un modo de caracteres, así como PostScript o como se llame la interfaz de Hewlett Packard, y para lanzar una página se envía un formulario.
Borodin
1
@Borodin Hewlett Packard se llama PCL (Lenguaje de control de la impresora).
CB_Ron

Respuestas:

182

Las versiones de Perl 5.10 y posteriores admiten clases de caracteres verticales y horizontales subsidiarias \vy \h, además, la clase de caracteres de espacio en blanco genérico\s

La solución más limpia es usar la clase de caracteres de espacio en blanco horizontal\h . Esto coincidirá con la pestaña y el espacio del conjunto ASCII, el espacio sin interrupción del ASCII extendido o cualquiera de estos caracteres Unicode

U+0009 CHARACTER TABULATION
U+0020 SPACE
U+00A0 NO-BREAK SPACE (not matched by \s)

U+1680 OGHAM SPACE MARK
U+2000 EN QUAD
U+2001 EM QUAD
U+2002 EN SPACE
U+2003 EM SPACE
U+2004 THREE-PER-EM SPACE
U+2005 FOUR-PER-EM SPACE
U+2006 SIX-PER-EM SPACE
U+2007 FIGURE SPACE
U+2008 PUNCTUATION SPACE
U+2009 THIN SPACE
U+200A HAIR SPACE
U+202F NARROW NO-BREAK SPACE
U+205F MEDIUM MATHEMATICAL SPACE
U+3000 IDEOGRAPHIC SPACE

El patrón de espacio vertical\v es menos útil, pero coincide con estos caracteres.

U+000A LINE FEED
U+000B LINE TABULATION
U+000C FORM FEED
U+000D CARRIAGE RETURN
U+0085 NEXT LINE (not matched by \s)

U+2028 LINE SEPARATOR
U+2029 PARAGRAPH SEPARATOR

Hay siete caracteres de espacio en blanco vertical que coinciden \vy dieciocho caracteres horizontales que coinciden \h. \scoincide con veintitrés personajes

Todos los caracteres de espacio en blanco son verticales u horizontales sin superposición, pero no son subconjuntos adecuados porque \htambién coinciden con U + 00A0 ESPACIO SIN INTERRUPCIONES, y \vtambién coinciden con U + 0085 LÍNEA SIGUIENTE, ninguno de los cuales coincide con\s

Borodina
fuente
77
\hfunciona solo en los idiomas que admite PCRE.
Avinash Raj
14
@AvinashRaj: Esta pregunta es sobre Perl, que ciertamente es compatible con PCRE
Borodin
2
@AvinashRaj: Excepto que [[:blank:]]no coincide con ninguna ruptura espacio--  o"\xA0"
Borodin
66
Quiero mencionar que \hfuncionó perfectamente para mi caso de uso, que estaba haciendo una búsqueda / reemplazo en Notepad ++ en 1 o más espacios contiguos de líneas no nuevas. Nada más (simple) funcionó.
squidbe
8
Lo que hace que Perl sea \hligeramente no estándar es su inclusión MONGOLIAN VOWEL SEPARATOR. Unicode no lo considera espacio en blanco. Por esa razón, Perl \hdifiere de POSIX blank( [[:blank:]]en Perl, \p{Blank}en Java) y Java 8 \h. Es cierto que es un caso extremo.
Aleksandr Dubinsky
362

Use un doble negativo:

/[^\S\r\n]/

Es decir, no espacio en blanco (la S mayúscula se complementa) o no retorno de carro o no nueva línea. Distribuyendo el exterior no ( es decir , el complemento ^en la clase de caracteres) con la ley de De Morgan , esto es equivalente a "espacio en blanco pero no retorno de carro o nueva línea". La inclusión de ambos \ry \nen el patrón maneja correctamente todas las convenciones de nueva línea de Unix (LF), Mac OS (CR) clásico y DOS-ish (CR LF) .

No es necesario tomar mi palabra:

#! /usr/bin/env perl

use strict;
use warnings;

use 5.005;  # for qr//

my $ws_not_crlf = qr/[^\S\r\n]/;

for (' ', '\f', '\t', '\r', '\n') {
  my $qq = qq["$_"];
  printf "%-4s => %s\n", $qq,
    (eval $qq) =~ $ws_not_crlf ? "match" : "no match";
}

Salida:

"" => coincidencia
"\ f" => coincidencia
"\ t" => coincidencia
"\ r" => sin coincidencia
"\ n" => sin coincidencia

Tenga en cuenta la exclusión de la pestaña vertical, pero esto se aborda en v5.18 .

Antes de objetar con demasiada dureza, la documentación de Perl usa la misma técnica. Una nota al pie en la sección "Espacio en blanco" de perlrecharclass lee

Antes de Perl v5.18, \sno coincidía con la pestaña vertical. [^\S\cK](oscuramente) coincide con lo que \stradicionalmente hacía.

La misma sección de perlrecharclass también sugiere otros enfoques que no ofenden la oposición de los profesores de idiomas a los negativos dobles.

Fuera de la configuración regional y las reglas Unicode o cuando el /acambio está en vigor, " \scoincide [\t\n\f\r ]y, comenzando en Perl v5.18, la pestaña vertical \cK". Deseche \ry \ndeje /[\t\f\cK ]/para espacios en blanco coincidentes pero no para nueva línea.

Si su texto es Unicode, use un código similar al siguiente para construir un patrón de la tabla en la sección de documentación mencionada anteriormente .

sub ws_not_nl {
  local($_) = <<'EOTable';
0x0009        CHARACTER TABULATION   h s
0x000a              LINE FEED (LF)    vs
0x000b             LINE TABULATION    vs  [1]
0x000c              FORM FEED (FF)    vs
0x000d        CARRIAGE RETURN (CR)    vs
0x0020                       SPACE   h s
0x0085             NEXT LINE (NEL)    vs  [2]
0x00a0              NO-BREAK SPACE   h s  [2]
0x1680            OGHAM SPACE MARK   h s
0x2000                     EN QUAD   h s
0x2001                     EM QUAD   h s
0x2002                    EN SPACE   h s
0x2003                    EM SPACE   h s
0x2004          THREE-PER-EM SPACE   h s
0x2005           FOUR-PER-EM SPACE   h s
0x2006            SIX-PER-EM SPACE   h s
0x2007                FIGURE SPACE   h s
0x2008           PUNCTUATION SPACE   h s
0x2009                  THIN SPACE   h s
0x200a                  HAIR SPACE   h s
0x2028              LINE SEPARATOR    vs
0x2029         PARAGRAPH SEPARATOR    vs
0x202f       NARROW NO-BREAK SPACE   h s
0x205f   MEDIUM MATHEMATICAL SPACE   h s
0x3000           IDEOGRAPHIC SPACE   h s
EOTable

  my $class;
  while (/^0x([0-9a-f]{4})\s+([A-Z\s]+)/mg) {
    my($hex,$name) = ($1,$2);
    next if $name =~ /\b(?:CR|NL|NEL|SEPARATOR)\b/;
    $class .= "\\N{U+$hex}";
  }

  qr/[$class]/u;
}

Otras aplicaciones

El truco doble negativo también es útil para hacer coincidir los caracteres alfabéticos. Recuerde que \wcoincide con "caracteres de palabras", caracteres alfabéticos y dígitos y guiones bajos. Los estadounidenses feos a veces queremos escribirlo como, digamos,

if (/[A-Za-z]+/) { ... }

pero una clase de caracteres doble negativa puede respetar la configuración regional:

if (/[^\W\d_]+/) { ... }

Expresar "un carácter de palabra pero no un dígito o guión bajo" de esta manera es un poco opaco. Una clase de caracteres POSIX comunica la intención más directamente

if (/[[:alpha:]]+/) { ... }

o con una propiedad Unicode como szbalint sugirió

if (/\p{Letter}+/) { ... }
Greg Bacon
fuente
44
Inteligente, pero el comportamiento es muy sorprendente, y no veo cómo es menos incómodo.
Qwertie
77
@Qwertie: ¿qué es sorprendente? ¿Menos incómodo que qué?
Ysth
9
Excelentemente horrible
9
Esto es muy bueno. Según lo solicitado, usted hace coincidir el espacio en blanco (no solo algunos caracteres de espacio en blanco) y excluye el carácter de avance de línea. Su solución no tiene que ver con la pregunta: "qué caracteres de espacios en blanco existen", como no debería. Esto es precisamente lo que estaba buscando. (Como se ha señalado por @Rory, una 'nueva línea' también puede incluir \r, por ejemplo, en Windows, por lo que considerar exluding los del partido, así: /[^\S\r\n]/)
Timo
1
Esto sin duda satisfará las necesidades del OP y de prácticamente todos los demás que buscan esta pregunta (de todos modos, hablantes de inglés). Pero sigue siendo una mala respuesta. Simplemente no hay excusa para usar esta solución cuando \hestá disponible.
Alan Moore
50

Una variación de la respuesta de Greg que también incluye retornos de carro:

/[^\S\r\n]/

Esta expresión regular es más segura que /[^\S\n]/con no \r. Mi razonamiento es que Windows usa \r\npara las nuevas líneas y Mac OS 9 \r. Es poco probable que lo encuentres \rsin \nhoy en día, pero si lo encuentras, no podría significar nada más que una nueva línea. Por lo tanto, dado que \rpuede significar una nueva línea, también debemos excluirla.

Rory O'Kane
fuente
1
La solución de +1 Greg terminó corrompiendo mi texto, el tuyo funcionó bien.
Timo Huovinen
Es posible que se sorprenda de cuántos programas todavía usan "\ r" para los finales de línea. A veces me tomó un tiempo darme cuenta de que mi problema era que el archivo los usaba. O que usó la codificación de caracteres MacRoman ...
mivk
2
parece que @Greg primero lo hizo "incorrecto", lo cambió y no le dio crédito. Por eso estoy votando aquí.
Andre Elrico
14

La expresión regular a continuación coincidiría con espacios en blanco pero no con un nuevo carácter de línea.

(?:(?!\n)\s)

MANIFESTACIÓN

Si desea agregar el retorno de carro también, agregue \rcon el |operador dentro de la búsqueda anticipada negativa.

(?:(?![\n\r])\s)

MANIFESTACIÓN

Agregue +después del grupo sin captura para que coincida con uno o más espacios en blanco.

(?:(?![\n\r])\s)+

MANIFESTACIÓN

No sé por qué no mencionaron la clase de caracteres POSIX [[:blank:]]que coincide con espacios en blanco horizontales ( espacios y tabulaciones ). Esta clase de caracteres POSIX funcionaría en BRE ( expresiones regulares básicas ), ERE ( expresión regular extendida ), PCRE ( expresión regular compatible con Perl ).

MANIFESTACIÓN

Avinash Raj
fuente
¡Esta es la mejor solución!
loretoparisi
13

Lo que está buscando es la blankclase de caracteres POSIX . En Perl se hace referencia como:

[[:blank:]]

en Java (no olvide habilitar UNICODE_CHARACTER_CLASS):

\p{Blank}

En comparación con similares \h, POSIX blankes compatible con algunos motores regex más ( referencia ). Un beneficio importante es que su definición se fija en el Anexo C: Propiedades de compatibilidad de las expresiones regulares Unicode y estándar en todos los sabores de expresiones regulares que admiten Unicode. (En Perl, por ejemplo, \hopta por incluir adicionalmente el MONGOLIAN VOWEL SEPARATOR.) Sin embargo, un argumento a favor \hes que siempre detecta los caracteres Unicode (incluso si los motores no están de acuerdo en cuál), mientras que las clases de caracteres POSIX a menudo son ASCII por defecto -solo (como en Java).

Pero el problema es que incluso apegarse a Unicode no resuelve el problema al 100%. Considere los siguientes caracteres que no se consideran espacios en blanco en Unicode:

  • SEPARADOR DE VOCALES MONGOLIANAS U + 180E

  • U + 200B ESPACIO ANCHO CERO

  • ANCHO CERO U + 200C SIN UNIÓN

  • UNIDAD DE ANCHO CERO U + 200D

  • U + 2060 WORD JOINER

  • U + FEFF ANCHO CERO ESPACIO SIN INTERRUPCIONES

    Tomado de https://en.wikipedia.org/wiki/White-space_character

El mencionado separador de vocales de Mongolia no está incluido por lo que probablemente sea una buena razón. Esto, junto con 200C y 200D, se produce dentro de las palabras (AFAIK) y, por lo tanto, rompe la regla cardinal que todos los demás espacios en blanco obedecen: puede tokenizar con ella. Son más como modificadores. Sin embargo, ZERO WIDTH SPACE, WORD JOINER, y ZERO WIDTH NON-BREAKING SPACE(si se utiliza como distinto de una marca de orden de bytes) ajustarse a la regla de los espacios en blanco en mi libro. Por lo tanto, los incluyo en mi clase de caracteres de espacio en blanco horizontal.

En Java:

static public final String HORIZONTAL_WHITESPACE = "[\\p{Blank}\\u200B\\u2060\\uFFEF]"
Aleksandr Dubinsky
fuente
Debe agregar los indicadores de compilación regexp apropiados a la compilación de Java y ejecutar Java 7 o posterior. En cualquier caso, la pregunta no era sobre Java o PCRE, por lo que todo esto es irrelevante.
tchrist
@tchrist Gracias por señalar esto. Actualizaré mi respuesta. Sin embargo, no estoy de acuerdo con que mi respuesta sea irrelevante. Lo que no tiene importancia es la perletiqueta en la pregunta original.
Aleksandr Dubinsky
1
@AleksandrDubinsky, \ p {Blank} no es compatible con JavaScript, por lo que definitivamente no es "estándar para todos los sabores de expresiones regulares" -1
Valentin Vasilyev
Más informativo Me resulta inquietante saber que no existe una clase general y completa de caracteres abreviados de "espacio en blanco horizontal", y que [\p{Blank}\u200b\u180e]se requieren horrores como . Es cierto que tiene sentido que un separador de vocales no se considere un espacio en blanco, pero por qué el espacio de ancho cero no está en clases como \sy \p{Blank}, me gana.
Timo
Seguimiento: leí que ambos se consideran 'límites neutrales', aunque eso no explica por qué .
Timo
-4

m/ /gsolo dale espacio / /y funcionará. O use \S: reemplazará todos los caracteres especiales como tabulación, líneas nuevas, espacios, etc.

saiprathapreddy.obula
fuente