Corresponder espaço em branco, mas não novas linhas

277

Às vezes, eu quero combinar espaço em branco, mas não nova linha.

Até agora eu tenho recorrido [ \t]. Existe uma maneira menos estranha?

JoelFan
fonte
4
BTW, esses personagens também são "espaços em branco": [\r\f].
Eugene Yarmash
2
@eugeney alguém ainda está fazendo feeds de formulário? (\ f's)
Aran Mulholland
1
@AranMulholland: qualquer pessoa que possua uma impressora orientada a caracteres. A maioria das impressoras possui um modo de caractere, assim como PostScript ou qualquer que seja a interface da Hewlett Packard, e para lançar uma página, você envia um feed de formulário.
Borodin
1
A @Borodin Hewlett Packard é chamada PCL (Printer Control Language).
CB_Ron

Respostas:

182

As versões 5.10 e posteriores do Perl suportam classes de caracteres verticais e horizontais subsidiárias \ve \h, assim como a classe de caracteres de espaço em branco genérica\s

A solução mais limpa é usar a classe de caracteres de espaço em branco horizontal\h . Isso corresponderá a tabulação e espaço do conjunto ASCII, espaço ininterrupto do ASCII estendido ou qualquer um desses 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

O padrão de espaço vertical\v é menos útil, mas corresponde a esses 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

Existem sete caracteres de espaço em branco verticais que correspondem \ve dezoito caracteres horizontais que correspondem \h. \scorresponde a vinte e três caracteres

Todos os caracteres de espaço em branco são verticais ou horizontais, sem sobreposição, mas não são subconjuntos adequados porque \htambém correspondem a U + 00A0 NO-BREAK ESPAÇO e \vtambém correspondem a U + 0085 NEXT LINE, nenhum dos quais corresponde a\s

Borodin
fonte
7
\hfunciona apenas nos idiomas suportados PCRE.
Avinash Raj
14
@AvinashRaj: Esta pergunta é sobre Perl, que certamente suporta PCRE
Borodin
2
@AvinashRaj: Exceto que [[:blank:]]não corresponde ao espaço sem interrupção -  ou"\xA0"
Borodin
6
Quero mencionar que \hfuncionou perfeitamente para o meu caso de uso, que estava encontrando / substituindo no Notepad ++ em 1 ou mais espaços contíguos que não são de nova linha. Nada mais (simples) funcionou.
squidbe
8
O que torna o Perl um \hpouco fora do padrão é sua inclusão MONGOLIAN VOWEL SEPARATOR. Unicode não considera espaço em branco. Por esse motivo, o Perl \hdifere do POSIX blank( [[:blank:]]no Perl, \p{Blank}no Java) e do Java 8 \h. É certo que é um caso de ponta.
Aleksandr Dubinsky
362

Use um negativo duplo:

/[^\S\r\n]/

Ou seja, não-espaço em branco (o capital S complementa) ou não-retorno de carro ou não-nova linha. Distribuir o não externo ( isto é , o complemento ^na classe de personagem) com a lei de De Morgan , é equivalente a "espaço em branco, mas não retorno de carro ou nova linha". A inclusão de ambos \re \nno padrão manipula corretamente todas as convenções de nova linha do Unix (LF), Mac OS (CR) clássico e DOS-ish (CR LF) .

Não há necessidade de aceitar minha palavra:

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

Resultado:

"" => correspondência
"\ f" => correspondência
"\ t" => correspondência
"\ r" => sem correspondência
"\ n" => sem correspondência

Observe a exclusão da guia vertical, mas isso é abordado na v5.18 .

Antes de objetar com muita severidade, a documentação do Perl usa a mesma técnica. Uma nota de rodapé na seção "Espaço em branco" da perlrecharclass

Antes do Perl v5.18, \snão correspondia à guia vertical. [^\S\cK](obscuramente) corresponde ao que \stradicionalmente fazia.

A mesma seção da perlrecharclass também sugere outras abordagens que não ofendem a oposição dos professores de línguas aos negativos duplos.

Fora das regras de código de idioma e Unicode ou quando a /aopção estiver em vigor, " \scorresponde [\t\n\f\r ]e, a partir do Perl v5.18, a guia vertical \cK". Descarte \re \ndeixe o /[\t\f\cK ]/espaço em branco correspondente, mas não a nova linha.

Se o seu texto for Unicode, use um código semelhante ao sub abaixo para construir um padrão a partir da tabela na seção de documentação mencionada acima .

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

Outras aplicações

O truque de negativo duplo também é útil para combinar caracteres alfabéticos também. Lembre-se de que \wcorresponde a "caracteres da palavra", caracteres e dígitos alfabéticos e sublinhado. Nós, americanos feios, às vezes queremos escrever como, digamos,

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

mas uma classe de caracteres dupla negativa pode respeitar o código do idioma:

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

Expressar "um caractere de palavra, mas não um dígito ou sublinhado" dessa maneira é um pouco opaco. Uma classe de caracteres POSIX comunica a intenção mais diretamente

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

ou com uma propriedade Unicode, conforme sugerido pelo szbalint

if (/\p{Letter}+/) { ... }
Greg Bacon
fonte
4
Inteligente, mas o comportamento é muito surpreendente, e não vejo como é menos estranho.
Qwertie
7
@ Qwertie: o que é surpreendente? Menos estranho do que o que?
ysth 12/08/10
9
Excelentemente horrível.
9
Isso é muito bom. Conforme solicitado, você corresponde ao espaço em branco (não apenas a alguns caracteres em branco) e exclui o caractere de avanço de linha. Sua solução não se preocupa com a pergunta: "que caracteres de espaço em branco existem", como não deveria. Era exatamente isso que eu estava procurando. (Como observado por @Rory, uma 'nova linha' também pode incluir \r, por exemplo, no Windows, por isso considero exluding aqueles do jogo, bem como: /[^\S\r\n]/)
Timo
1
Isso certamente atenderá às necessidades do OP e praticamente a todos os que pesquisam essa pergunta (de qualquer forma, falantes de inglês). Mas ainda é uma resposta ruim. Simplesmente não há desculpa para usar esta solução quando \hestiver disponível.
Alan Moore
50

Uma variação na resposta de Greg que também inclui retornos de carro:

/[^\S\r\n]/

Esse regex é mais seguro do que /[^\S\n]/sem \r. Meu raciocínio é que o Windows usa \r\npara novas linhas e o Mac OS 9 usado \r. Você é improvável encontrar \rsem \nnos dias de hoje, mas se você encontrá-lo, ele não podia nada média, mas uma nova linha. Assim, como \rpode significar uma nova linha, devemos excluí-la também.

Rory O'Kane
fonte
1
+1 solução de Greg acabou corrompendo o meu texto, seu funcionou bem.
Timo Huovinen
Você pode se surpreender com a quantidade de programas que ainda usam "\ r" para fins de linha. Às vezes, levava um tempo para descobrir que meu problema era que o arquivo as usava. Ou que ele usou a codificação de caracteres MacRoman ...
mivk
2
parece que o @Greg primeiro o "errado" o alterou e não deu crédito a você. É por isso que estou votando aqui.
Andre Elrico 31/03
14

O regex abaixo corresponderia a espaços em branco, mas não a um novo caractere de linha.

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

DEMO

Se você deseja adicionar retorno de carro também, adicione \ro |operador dentro da cabeça negativa.

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

DEMO

Adicione +depois do grupo que não captura para corresponder a um ou mais espaços em branco.

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

DEMO

Não sei por que as pessoas falharam em mencionar a classe de caracteres POSIX [[:blank:]]que corresponde a qualquer espaço em branco horizontal ( espaços e tabulações ). Essa classe chracter POSIX funcionaria em BRE ( Expressões regulares regulares ), ERE ( Expressão regular estendida ), PCRE ( Expressão regular compatível com Perl ).

DEMO

Avinash Raj
fonte
Essa é a melhor solução!
loretoparisi
13

O que você está procurando é a blankclasse de caracteres POSIX . No Perl, é referenciado como:

[[:blank:]]

em Java (não se esqueça de ativar UNICODE_CHARACTER_CLASS):

\p{Blank}

Comparado com o similar \h, o POSIX blanké suportado por mais alguns mecanismos regex ( referência ). Um benefício importante é que sua definição é fixada no Anexo C: Propriedades de Compatibilidade de Expressões Regulares Unicode e padrão em todos os tipos de expressões regulares que suportam Unicode. (No Perl, por exemplo, \hescolhe incluir adicionalmente o MONGOLIAN VOWEL SEPARATOR.) No entanto, um argumento a favor \hé que ele sempre detecta caracteres Unicode (mesmo que os mecanismos não concordem com quais), enquanto as classes de caracteres POSIX geralmente são ASCII padrão. -só (como em Java).

Mas o problema é que mesmo a adesão ao Unicode não resolve o problema 100%. Considere os seguintes caracteres que não são considerados espaços em branco no Unicode:

  • SEPARADOR MONGOLIANO DE VOWEL U + 180E

  • ESPAÇO ZERO DA LARGURA DE U + 200B

  • NÃO-ARTICULADOR COM LARGURA ZERO U + 200C

  • JUNTA DE LARGURA ZERO U + 200D

  • JOINADOR DE PALAVRAS U + 2060

  • ESPAÇO U + FEFF ZERO LARGURA QUE NÃO SE VIRA

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

O separador de vogais mongol mencionado acima não está incluído pelo que provavelmente é um bom motivo. Juntamente com 200C e 200D, ocorre dentro de palavras (AFAIK) e, portanto, quebra a regra principal que todos os outros espaços em branco obedecem: é possível tokenizar com ele. Eles são mais como modificadores. No entanto, ZERO WIDTH SPACE, WORD JOINER, e ZERO WIDTH NON-BREAKING SPACE(se usado como diferente de uma marca de ordem de byte) se ajustar à regra de espaço em branco em meu livro. Portanto, eu os incluo na minha classe de caracteres de espaço em branco horizontal.

Em Java:

static public final String HORIZONTAL_WHITESPACE = "[\\p{Blank}\\u200B\\u2060\\uFFEF]"
Aleksandr Dubinsky
fonte
Você precisa adicionar os sinalizadores de compilação regexp apropriados à compilação Java e estar executando o Java 7 ou posterior. De qualquer forma, a questão não era sobre Java ou PCRE, portanto tudo isso é imaterial.
tchrist
@tchrist Obrigado por apontar isso. Vou atualizar minha resposta. Eu discordo, porém, que minha resposta é irrelevante. O que é imaterial é a perletiqueta na pergunta original.
Aleksandr Dubinsky
1
@AleksandrDubinsky, \ p {Blank} não é suportado em JavaScript, portanto, definitivamente não é "padrão para todos os sabores de regex" -1
Valentin Vasilyev
Mais informativo. Acho perturbador saber que não existe uma classe geral completa e completa de caracteres abreviados de "espaço em branco horizontal" e que horrores como esse [\p{Blank}\u200b\u180e]são necessários. É certo que faz sentido que um separador de vogal não seja considerado um caractere de espaço em branco, mas por que o espaço de largura zero não está em classes como \se \p{Blank}, me bate.
Timo
Acompanhamento: li que ambos são considerados "neutros em relação aos limites", embora isso não explique o porquê .
Timo
-4

m/ /gapenas ceda espaço / /e funcionará. Ou use \S- ele substituirá todos os caracteres especiais, como tabulação, novas linhas, espaços e assim por diante.

saiprathapreddy.obula
fonte