regex 匹配空格但不匹配换行符

mznpcxlj  于 2023-10-22  发布在  其他
关注(0)|答案(8)|浏览(104)

我有时想匹配空白,但不是换行符。
到目前为止,我一直在求助于[ \t]。有没有不那么尴尬的方法?

wfveoks0

wfveoks01#

摘要

  • 使用\h匹配水平空格,自v5.10.0(2007年发布)起在perl中使用
  • 对于非PCRE引擎,请使用双重否定:[^\S\r\n]
  • Unicode属性:\p{Blank}\p{HorizSpace}
  • 在ASCII中直接:[\t\f\cK ]
  • 直接,在Unicode中(但不要,真的)
  • 双重否定和Unicode属性的其他应用

水平空白

perlre的“字符类和其他特殊转义”部分包括

  • \h水平空白
  • \H不是水平空白

双重否定

如果你可能会在其他引擎上使用你的模式,特别是那些不是Perl-compatible或者不支持\h的引擎,那么用双重否定来表达它:

[^\S\r\n]

也就是说,not-not-whitespace(大写的S补码)或not-carriage-return或not-newline。用De Morgan’s law分配外部not(* 即括号中的字符类中的补码^),这相当于从\s中减去\r\n。在模式中同时包含回车符和换行符可以正确处理所有Unix(LF)、经典Mac OS(CR)和DOS风格(CRLF)的换行符约定。
不用相信我的话:

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

输出量:

" "  => match
"\f" => match
"\t" => match
"\r" => no match
"\n" => no match

注意排除了垂直选项卡,但这在v5.18中得到了解决。
在过于苛刻地反对之前,Perl文档使用了相同的技术。perlrecharclass的“空白”部分中的一个脚注是这样的:
在Perl v5.18之前,\s与垂直选项卡不匹配。[^\S\cK](模糊地)与\s传统上所做的相匹配。

Unicode属性

前面提到的关于\h\H的perlre文档引用了perlunicode文档,我们在那里读到了一系列有用的Unicode属性。

  • \p{Blank}
  • 这与\h\p{HorizSpace}相同:水平改变间距的字符。
  • \p{HorizSpace}
  • 这与\h\p{Blank}相同:水平改变间距的字符。

直接方式:ASCII版本

perlrecharclass的“空白”部分还建议了其他一些方法,这些方法不会冒犯语法老师对双重否定的反对。
说出你想要的而不是你不想要的。
在区域设置和Unicode规则之外,或者当/a/aa开关生效时,“\s匹配[\t\n\f\r ],并且从Perl v5.18开始,垂直制表符\cK”。
要匹配空格而不匹配换行符(广义上),请丢弃\r\n以保留

[\t\f\cK ]

直接方式:Unicode版本

如果您的文本是Unicode,请使用与下面的sub类似的代码从perlrecharclass的“Whitespace”部分的表中构造一个模式。

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

以上是为了完整性。使用Unicode属性,而不是手写。

其他应用

双重否定的技巧对于匹配字母字符也很方便。请记住,\w匹配“单词字符”、字母字符 * 和 * 数字以及下划线。我们丑陋的美国人有时想把它写成,比如说,

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

但双重否定字符类可以尊重区域设置:

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

用这种方式表示“一个单词字符,而不是数字或下划线”有点不透明。POSIX字符类更直接地传达意图

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

或者使用szbalint建议的Unicode属性

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

Pingui询问有关嵌套双重否定字符类以有效修改

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

我能想到的最好的方法是使用|作为替代,并将\s移动到另一个分支:

/(\+|0|\()(?:[\d()-]|[^\S\r\n]){6,20}\d/g
ctzwtxfj

ctzwtxfj2#

perl5.10和更高版本支持辅助的垂直和水平字符类\v\h,以及通用的空白字符类\s
最干净的解决方案是使用 horizontal whitespace 字符类\h。这将匹配ASCII集合中的制表符和空格、扩展ASCII中的不间断空格或任何这些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
  • vertical space* 模式\v不太有用,但可以匹配以下字符
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

有7个垂直空格字符匹配\v,18个水平空格字符匹配\h\s匹配23个字符
所有空白字符都是 * 垂直 * 或 * 水平 *,没有重叠,但它们不是真子集,因为\h还匹配U+00 A0 NO-BREAK SPACE,\v还匹配U+0085 NEXT LINE,\s不匹配这两个字符

jaxagkaj

jaxagkaj3#

Greg’s answer的一个变体,也包括回车:

/[^\S\r\n]/

这个正则表达式比没有\r/[^\S\n]/更安全。我的理由是,Windows使用\r\n作为换行符,Mac OS 9使用\r。现在你不太可能找到不带\n\r,但是如果你找到了,它只意味着一个换行符。因此,由于\r可以表示换行符,我们也应该排除它。

yjghlzjz

yjghlzjz4#

下面的正则表达式将匹配白色空格,但不匹配新行字符。

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

DEMO
如果你还想添加回车符,那么在负的lookahead中添加||运算符。

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

DEMO
在非捕获组之后添加+,以匹配一个或多个白色空格。

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

DEMO
我不知道为什么你们没有提到POSIX字符类[[:blank:]],它匹配任何水平空格(* 空格和制表符 *)。这个POSIX字符类可以在BRE(Basic Regular Expressions),ERE(Extended Regular Expression),PCRE(Perl Compatible Regular Expression)上工作。
DEMO

4urapxun

4urapxun5#

您正在寻找的是POSIX blank字符类。在Perl中,它被引用为:

[[:blank:]]

在Java中(不要忘记启用UNICODE_CHARACTER_CLASS):

\p{Blank}

与类似的\h相比,POSIX blank被更多的正则表达式引擎(reference)支持。一个主要的好处是其定义固定在附件C中:Unicode正则表达式和所有支持Unicode的正则表达式的标准的兼容性属性。(例如,在Perl中,\h选择额外包含MONGOLIAN VOWEL SEPARATOR。)然而,支持\h的一个论点是它总是检测Unicode字符(即使引擎不同意),而POSIX字符类通常默认仅用于ASCII(如Java中)。
但问题是,即使坚持Unicode也不能100%解决这个问题。考虑以下在Unicode中不被视为空白的字符:

  • U+180E蒙古卷分离器
  • U+200B零宽度空间
  • U+200C零宽度非接头
  • U+200D零宽度接头
  • U+2060 WORD JOINER
  • U+FEFF零宽不间断空间

来源:https://en.wikipedia.org/wiki/White-space_character
前面提到的蒙古语元音分隔符没有包括在内,这可能是一个很好的理由。它与200 C和200 D一起沿着出现在单词(AFAIK)中,因此打破了所有其他空白都遵守的基本规则:你可以用它来标记。它们更像是修饰语。但是,ZERO WIDTH SPACEWORD JOINERZERO WIDTH NON-BREAKING SPACE(如果它不是用作字节顺序标记)符合我书中的空白规则。因此,我将它们包含在水平空白字符类中。
在Java中:

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

00jrzges6#

将下面的正则表达式放在 find 部分,并从“Search Mode”中选择 Regular Expression

[^\S\r\n]+
eoxn13cs

eoxn13cs7#

你可能想要\h,就像其他人指出的那样。但是,Perl v5.18 and later supports regex set operations作为其Unicode支持的一部分。如果你想要某样东西的大部分,减去你不想要的那几样东西可能更容易。
假设您接受除了换行符之外的任何空格。你不关心回车、换页或者垂直制表符。这个正则表达式集合操作通过从所有空格开始并删除换行符来创建一个字符类:

use v5.18; 
/(?[ [\s] - [\n] ])/;

还有一个假设你想要所有的拉丁字母除了元音。你可以把它和遗漏写出来,希望你不会出错:

/[b-df-hj-np-tv-z]/;

当代码清楚地显示您正在做的事情时,这会更容易:

use v5.18; 
/(?[ [a-z] - [aeiou] ])/;
eni9jsuy

eni9jsuy8#

m/ /g只是在/ /中给给予空间,它就能工作。或者使用\S-它将替换所有特殊字符,如制表符,换行符,空格等。

相关问题