Coincidir con espacios en blanco pero no con nuevas líneas

Resuelto JoelFan asked hace 14 años • 8 respuestas

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

Hasta ahora he estado recurriendo a [ \t]. ¿Existe una forma menos incómoda?

JoelFan avatar Aug 12 '10 22:08 JoelFan
Aceptado

Resumen

  • Úselo \hpara hacer coincidir espacios en blanco horizontales, en Perl desde v5.10.0 (lanzado en 2007)
  • Para motores que no sean PCRE , utilice un doble negativo:[^\S\r\n]
  • Propiedades Unicode: \p{Blank}o\p{HorizSpace}
  • Sea directo, en ASCII:[\t\f\cK ]
  • Sea directo, en Unicode (pero no lo haga, en realidad)
  • Otras aplicaciones de dobles negativos y propiedades Unicode

Espacios en blanco horizontales

La sección "Clases de personajes y otros escapes especiales" de perlre incluye

  • \hespacios en blanco horizontales
  • \HNo espacios en blanco horizontales

Doble negativa

Si puede utilizar su patrón con otros motores, particularmente aquellos que no son compatibles con Perl o que no admiten \h, expréselo como un doble negativo:

[^\S\r\n]

Es decir, no-no-espacios en blanco (los Scomplementos mayúsculas) o no-retorno-de-carro o no-nueva línea. Distribuir el not externo ( es decir , el complemento ^en la clase de caracteres entre corchetes ) con la ley de De Morgan , esto equivale a restar \ry \nde \s. La inclusión de retorno de carro y nueva línea en el patrón maneja correctamente todas las convenciones de nueva línea de Unix (LF), Mac OS clásico (CR) y DOS-ish (CRLF) .

No es necesario que confíes en mi palabra:

#! /usr/bin/env perl

use strict;
use warnings;

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";
}

Producción:

" " => coincidencia
"\f" => coincidencia
"\t" => coincidencia
"\r" => no coincide
"\n" => no coincide

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

Antes de objetar demasiado duramente, la documentación de Perl utiliza la misma técnica. Una nota a pie de página en la sección "Espacio en blanco" de perlrecharclass dice

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


Propiedades Unicode

La documentación de Perlre antes mencionada \hhace\H referencia a la documentación de Perlunicode donde leemos sobre una familia de propiedades Unicode útiles.

  • \p{Blank}
    • Esto es lo mismo que \hy \p{HorizSpace}: un carácter que cambia el espaciado horizontalmente.
  • \p{HorizSpace}
    • Esto es lo mismo que \hy \p{Blank}: un carácter que cambia el espaciado horizontalmente.

El enfoque directo: edición ASCII

La sección "Espacio en blanco" de perlrecharclass también sugiere otros enfoques que no ofenderán la oposición de los profesores de gramática a los dobles negativos.

Di lo que quieres en lugar de lo que no quieres.

Fuera de las reglas locales y Unicode o cuando el modificador /ao/aa está en vigor, " \scoincide con [\t\n\f\r ]y, a partir de Perl v5.18, la pestaña vertical \cK".

Para hacer coincidir los espacios en blanco pero no las nuevas líneas (en sentido amplio), descarte \ry \nsalga

[\t\f\cK ]

El enfoque directo: edición Unicode

Si su texto es Unicode, use un código similar al siguiente para construir un patrón a partir de la tabla en la sección "Espacio en blanco" de perlrecharclass .

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;
}

Lo anterior es para completar. Utilice las propiedades Unicode en lugar de escribirlas a mano.


Otras aplicaciones

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

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

pero una clase de carácter doble negativo puede respetar la configuración regional:

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

Expresar “un carácter de palabra pero no un dígito ni un guión bajo” de esta manera es un poco opaco. Una clase de caracteres POSIX comunica la intención de forma más directa

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

o con una propiedad Unicode como sugirió szbalint

if (/\p{Letter}+/) { ... }

Pingui preguntó acerca de anidar la clase de carácter doble negativo para modificar efectivamente \sel

/(\+|0|\()[\d()\s-]{6,20}\d/g

Lo mejor que se me ocurrió es utilizar |una alternativa y moverla \sa la otra rama:

/(\+|0|\()(?:[\d()-]|[^\S\r\n]){6,20}\d/g
Greg Bacon avatar Aug 12 '2010 15:08 Greg Bacon

Las versiones de Perl 5.10 y posteriores admiten clases de caracteres verticales y horizontales subsidiarias, \vasí \hcomo la clase genérica de caracteres de espacios en blanco.\s

La solución más limpia es utilizar la clase de carácter de espacio en blanco horizontal\h . Esto coincidirá con la tabulación y el espacio del conjunto ASCII, el espacio continuo 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 espacios en blanco verticales que coinciden \vy dieciocho caracteres horizontales que coinciden \h. \scoincide con veintitrés caracteres

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 INTERRUPCIÓN y \vtambién coinciden con U+0085 NEXT LINE, ninguno de los cuales coincide con\s

Borodin avatar Sep 21 '2014 07:09 Borodin

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]/sin \r. Mi razonamiento es que Windows usa \r\nnuevas líneas y Mac OS 9 usa \r. \rEs poco probable que lo encuentres \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 deberíamos excluirla.

Rory O'Kane avatar Jul 19 '2013 18:07 Rory O'Kane

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

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

MANIFESTACIÓN

Si también desea agregar retorno de carro, agregue \rcon el |operador dentro de la anticipación negativa.

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

MANIFESTACIÓN

Agregue +después del grupo que no 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 los 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 avatar Sep 21 '2014 01:09 Avinash Raj

Lo que buscas es la blankclase de caracteres POSIX. En Perl se hace referencia a él como:

[[:blank:]]

en Java (no olvide habilitar UNICODE_CHARACTER_CLASS):

\p{Blank}

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

Pero el problema es que ni siquiera seguir Unicode resuelve el problema al 100%. Considere los siguientes caracteres que no se consideran espacios en blanco en Unicode:

  • U+180E SEPARADOR DE VOCALES MONGOLIANAS

  • U+200B ESPACIO ANCHO CERO

  • U+200C ANCHO CERO NO EMBALAJE

  • CARPINTERÍA ANCHO CERO U+200D

  • U+2060 UNIÓN DE PALABRAS

  • U+FEFF ANCHO CERO ESPACIO IRRPONIBLE

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

El separador de vocales mongol antes mencionado no se incluye por lo que probablemente sea una buena razón. Este, junto con 200C y 200D, ocurre dentro de palabras (AFAIK) y, por lo tanto, rompe la regla fundamental que obedecen todos los demás espacios en blanco: puedes tokenizar con él. Son más como modificadores. Sin embargo, ZERO WIDTH SPACE, WORD JOINERy ZERO WIDTH NON-BREAKING SPACE(si se usa como algo distinto a una marca de orden de bytes) se ajustan a la regla de espacios en blanco de mi libro. Por lo tanto, los incluyo en mi clase de caracteres de espacios en blanco horizontales.

En Java:

static public final String HORIZONTAL_WHITESPACE = "[\\p{Blank}\\u200B\\u2060\\uFFEF]"
Aleksandr Dubinsky avatar Jul 28 '2014 18:07 Aleksandr Dubinsky