Abbina spazi bianchi ma non newline


277

A volte voglio abbinare spazi bianchi ma non newline.

Finora ho fatto ricorso [ \t]. C'è un modo meno imbarazzante?


4
BTW, questi personaggi sono anche "spazi bianchi": [\r\f].
Eugene Yarmash,

2
@eugeney qualcuno sta ancora facendo i feed dei moduli? (\ f's)
Aran Mulholland,

1
@AranMulholland: chiunque abbia una stampante orientata al personaggio. La maggior parte delle stampanti ha una modalità carattere oltre a PostScript o qualunque sia l'interfaccia Hewlett Packard e per lanciare una pagina si invia un feed di modulo.
Borodin,

1
@Borodin Hewlett Packard si chiama PCL (Printer Control Language).
CB_Ron,

Risposte:


182

Le versioni Perl 5.10 e successive supportano le classi di caratteri orizzontali e verticali secondarie \ve \h, oltre alla classe di caratteri generici degli spazi bianchi\s

La soluzione più pulita è utilizzare la classe di caratteri degli spazi bianchi orizzontali\h . Questo abbinerà tab e spazio dal set ASCII, spazio non interrotto da ASCII esteso o uno qualsiasi di questi caratteri 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

Il modello dello spazio verticale\v è meno utile, ma corrisponde a questi caratteri

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

Esistono sette caratteri verticali bianchi che corrispondono \ve diciotto orizzontali corrispondenti \h. \scorrisponde a 23 caratteri

Tutti i caratteri degli spazi bianchi sono verticali o orizzontali senza sovrapposizioni, ma non sono sottoinsiemi appropriati poiché \hcorrispondono anche a U + 00A0 NO-BREAK SPACE e \vanche a U + 0085 NEXT LINE, nessuno dei quali è associato da\s


7
\hfunziona solo sulle lingue che supporta PCRE.
Avinash Raj,

14
@AvinashRaj: Questa domanda riguarda Perl, che sicuramente supporta PCRE
Borodin

2
@AvinashRaj: Tranne che [[:blank:]]non corrisponde allo spazio no-break -  o"\xA0"
Borodin

6
Voglio dire che ha \hfunzionato perfettamente per il mio caso d'uso che stava facendo una ricerca / sostituzione in Notepad ++ su 1 o più spazi contigui non di nuova riga. Nient'altro (semplice) ha funzionato.
Squidbe,

8
Ciò che rende \hleggermente non standard Perl è la sua inclusione MONGOLIAN VOWEL SEPARATOR. Unicode non lo considera spazi bianchi. Per questo motivo, Perl \hdifferisce da POSIX blank( [[:blank:]]in Perl, \p{Blank}in Java) e Java 8 \h. Certo, è un caso limite.
Aleksandr Dubinsky,

362

Usa un doppio negativo:

/[^\S\r\n]/

Cioè, non-non-spazi bianchi (i complementi di S maiuscoli) o non-trasporto-ritorno o non-newline. Distribuire l'esterno non ( cioè il complemento ^nella classe personaggio) con la legge di De Morgan , questo equivale a "spazi bianchi ma non ritorno a capo o newline". Includere entrambi \re \nnel modello gestisce correttamente tutte le convenzioni newline di Unix (LF), Mac OS (CR) e DOS-ish (CR LF) .

Non c'è bisogno di crederci sulla parola:

#! /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";
}

Produzione:

"" => corrispondenza
"\ f" => match
"\ t" => match
"\ r" => nessuna corrispondenza
"\ n" => nessuna corrispondenza

Nota l'esclusione della scheda verticale, ma questo è risolto nella v5.18 .

Prima di obiettare troppo duramente, la documentazione Perl utilizza la stessa tecnica. Si legge una nota a piè di pagina nella sezione "Spazio bianco" della perlrecharclass

Prima di Perl v5.18, \snon corrispondeva alla scheda verticale. [^\S\cK](oscuramente) corrisponde a ciò che \stradizionalmente faceva.

La stessa sezione di perlrecharclass suggerisce anche altri approcci che non offenderanno l'opposizione degli insegnanti di lingue ai doppi negativi.

Fuori dalle regole locali e Unicode o quando l' opzione /aè attiva, " \scorrisponde [\t\n\f\r ]e, a partire da Perl v5.18, la scheda verticale \cK". Scarta \re \ndi lasciare /[\t\f\cK ]/per la corrispondenza spazi bianchi, ma non nuova riga.

Se il tuo testo è Unicode, utilizza un codice simile al sotto riportato di seguito per costruire un modello dalla tabella nella sezione della documentazione sopra menzionata .

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

Altre applicazioni

Il trucco a doppio negativo è utile anche per abbinare i caratteri alfabetici. Ricordate che \wle partite “caratteri di parola”, caratteri alfabetici e cifre e underscore. Noi brutti americani a volte vogliamo scriverlo come, diciamo,

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

ma una classe di caratteri a doppio negativo può rispettare la locale:

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

Esprimere "un carattere parola ma non cifra o carattere di sottolineatura" in questo modo è un po 'opaco. Una classe di caratteri POSIX comunica l'intento più direttamente

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

o con una proprietà Unicode come suggerito da szbalint

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

4
Intelligente, ma il comportamento è molto sorprendente e non vedo come sia meno imbarazzante.
Qwertie,

7
@Qwertie: cosa sorprende? Meno imbarazzante di cosa?
ysth

9
Eccellente terribile.

9
Questo va molto bene. Come richiesto, abbini spazi bianchi (non solo alcuni caratteri di spazi bianchi) ed escludi il carattere di avanzamento riga. La tua soluzione non si preoccupa della domanda: "quali caratteri degli spazi bianchi esistono", come non dovrebbe. Questo è esattamente quello che stavo cercando. (Come notato da @Rory, una 'nuova linea' può anche includere \r, ad esempio, su Windows, in modo da considerare esclusa la quelli della partita così: /[^\S\r\n]/)
Timo

1
Ciò soddisferà sicuramente le esigenze del PO e praticamente chiunque altro cerchi questa domanda (chi parla inglese, comunque). Ma è ancora una cattiva risposta. Semplicemente non ci sono scuse per l'utilizzo di questa soluzione quando \hè disponibile.
Alan Moore,

50

Una variazione sulla risposta di Greg che include anche i ritorni a capo:

/[^\S\r\n]/

Questa regex è più sicura che /[^\S\n]/con no \r. Il mio ragionamento è che Windows utilizza \r\nper le newline e Mac OS 9 utilizzato \r. È improbabile che lo trovi \rsenza \noggigiorno, ma se lo trovi, non potrebbe significare nient'altro che una nuova riga. Pertanto, poiché \rpuò significare una nuova riga, dovremmo escluderla anche noi.


1
+1 La soluzione di Greg ha finito per corrompere il mio testo, la tua ha funzionato bene.
Timo Huovinen,

Potresti essere sorpreso da quanti programmi usano ancora "\ r" per i finali di riga. A volte mi ci è voluto un po 'di tempo per capire che il mio problema era che il file li utilizzava. O che usasse la codifica del personaggio MacRoman ...
data

2
sembra che @Greg prima abbia sbagliato "cambiato" e non ti abbia accreditato. Ecco perché sto votando qui.
Andre Elrico,

14

La regex di seguito corrisponderebbe agli spazi bianchi ma non di un nuovo carattere di linea.

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

DEMO

Se si desidera aggiungere anche il ritorno a capo, aggiungere \rcon l' |operatore all'interno del punto di vista negativo.

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

DEMO

Aggiungi +dopo il gruppo non acquisito per abbinare uno o più spazi bianchi.

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

DEMO

Non so perché non avete menzionato la classe di caratteri POSIX [[:blank:]]che corrisponde a qualsiasi spazio bianco orizzontale ( spazi e tabulazioni ). Questa classe di caratteri POSIX funzionerebbe su BRE ( espressioni regolari di base ), ERE ( espressione regolare estesa ), PCRE ( espressione regolare compatibile Perl ).

DEMO


Questa è la soluzione migliore!
Loretoparisi,

13

Quello che stai cercando è la blankclasse di caratteri POSIX . In Perl è indicato come:

[[:blank:]]

in Java (non dimenticare di abilitare UNICODE_CHARACTER_CLASS):

\p{Blank}

Rispetto al simile \h, POSIX blankè supportato da alcuni motori regex in più ( riferimento ). Un grande vantaggio è che la sua definizione è fissata nell'Allegato C: Proprietà di compatibilità delle espressioni regolari Unicode e standard in tutti gli aromi regex che supportano Unicode. (In Perl, ad esempio, \hsceglie di includere ulteriormente il MONGOLIAN VOWEL SEPARATOR.) Tuttavia, un argomento a favore di \hè che rileva sempre i caratteri Unicode (anche se i motori non sono d'accordo su quale), mentre le classi di caratteri POSIX sono spesso di default ASCII -solo (come in Java).

Ma il problema è che anche attenersi a Unicode non risolve il problema al 100%. Considera i seguenti caratteri che non sono considerati spazi bianchi in Unicode:

  • SEPARATORE VOCALE MONGOLIANO U + 180E

  • U + 200B SPAZIO DI LARGHEZZA ZERO

  • U + 200C ZERO WIDTH NON JOINER

  • U + 200D ZERO WIDTH JOINER

  • U + 2060 WORD JOINER

  • U + FEFF ZERO WIDTH SPAZIO NON INTERRUTTORE

    Tratto da https://en.wikipedia.org/wiki/White-space_character

Il suddetto separatore vocale vocale mongolo non è incluso per quello che probabilmente è una buona ragione. Insieme a 200C e 200D, si presenta all'interno delle parole (AFAIK), e quindi infrange la regola cardinale a cui tutti gli altri spazi bianchi obbediscono: puoi tokenizzare con esso. Sono più come modificatori. Tuttavia, ZERO WIDTH SPACE, WORD JOINER, eZERO WIDTH NON-BREAKING SPACE (se usato come diverso da un segno di ordine di byte) montare la regola spazio bianco nel mio libro. Pertanto, li includo nella mia classe di caratteri degli spazi bianchi orizzontali.

In Java:

static public final String HORIZONTAL_WHITESPACE = "[\\p{Blank}\\u200B\\u2060\\uFFEF]"

È necessario aggiungere i flag di compilazione regexp appropriati alla compilazione Java ed eseguire Java 7 o versioni successive. In ogni caso, la domanda non riguardava affatto Java o PCRE, quindi tutto ciò è irrilevante.
tchrist,

@tchrist Grazie per averlo sottolineato. Aggiornerò la mia risposta. Non sono d'accordo, tuttavia, che la mia risposta sia irrilevante. Ciò che è irrilevante è il perltag nella domanda originale.
Aleksandr Dubinsky,

1
@AleksandrDubinsky, \ p {Blank} non è supportato in JavaScript, quindi sicuramente non "standard per tutti i sapori regex" -1
Valentin Vasilyev

Più informativo. Trovo inquietante sapere che non esiste una classe generale e completa di caratteri stenografici "spazi bianchi orizzontali" e che [\p{Blank}\u200b\u180e]sono richiesti orrori simili . Certo, ha senso che un separatore vocale non sia considerato un carattere di spazi bianchi, ma perché lo spazio a larghezza zero non è in classi come \se \p{Blank}, mi batte.
Timo,

Seguito: ho letto che entrambi sono considerati "neutrali al limite", sebbene ciò non spieghi il perché .
Timo,

-4

m/ /glascia spazio / /e funzionerà. Oppure usa \S: sostituirà tutti i caratteri speciali come tab, newline, spazi e così via.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.