空格匹配正则表达式 - Java

123
Java的正则表达式 API 表示 \s 可以匹配空格。因此,正则表达式 \\s\\s 应该匹配两个空格。
Pattern whitespace = Pattern.compile("\\s\\s");
matcher = whitespace.matcher(modLine);
while (matcher.find()) matcher.replaceAll(" ");

这是为了将所有连续的两个空格替换为一个空格。但实际上并不起作用。
我是否对正则表达式或“空格”一词有严重误解?

1
String类有一个replaceAll函数,可以帮你节省几行代码。http://download.oracle.com/javase/1.5.0/docs/api/java/lang/String.html - Zach L
1
这不是你的误解,而是Java的问题。尝试拆分像“abc \xA0 def \x85 xyz”这样的字符串,你就会明白我的意思:那里只有三个字段。 - tchrist
5
你尝试过使用 "\s+" 吗?这样可以将两个或更多的空格替换为一个。 - hrzafer
1
我已经想了一个多小时,为什么我的 \s 分割没有在空格上分割。非常感谢! - Marcin
11个回答

218

在Java中,您不能单独使用\s来匹配其本机字符集上的空格,因为Java不支持Unicode空格属性,尽管这样做严格要求满足UTS#18的RL1.2!。 Java所拥有的并不符合标准规范。

Unicode将26个代码点定义为\p{White_Space}:其中20个是各种类型的\pZGeneralCategory = Separator,其余6个是\p{Cc}GeneralCategory = Control

空格是一种相当稳定的属性,这些相同的位置几乎已经存在了很长时间。即便如此,在Java中没有符合Unicode标准的该属性,所以您必须使用类似以下的代码:

String whitespace_chars =  ""       /* dummy empty string for homogeneity */
                        + "\\u0009" // CHARACTER TABULATION
                        + "\\u000A" // LINE FEED (LF)
                        + "\\u000B" // LINE TABULATION
                        + "\\u000C" // FORM FEED (FF)
                        + "\\u000D" // CARRIAGE RETURN (CR)
                        + "\\u0020" // SPACE
                        + "\\u0085" // NEXT LINE (NEL) 
                        + "\\u00A0" // NO-BREAK SPACE
                        + "\\u1680" // OGHAM SPACE MARK
                        + "\\u180E" // MONGOLIAN VOWEL SEPARATOR
                        + "\\u2000" // EN QUAD 
                        + "\\u2001" // EM QUAD 
                        + "\\u2002" // EN SPACE
                        + "\\u2003" // EM SPACE
                        + "\\u2004" // THREE-PER-EM SPACE
                        + "\\u2005" // FOUR-PER-EM SPACE
                        + "\\u2006" // SIX-PER-EM SPACE
                        + "\\u2007" // FIGURE SPACE
                        + "\\u2008" // PUNCTUATION SPACE
                        + "\\u2009" // THIN SPACE
                        + "\\u200A" // HAIR SPACE
                        + "\\u2028" // LINE SEPARATOR
                        + "\\u2029" // PARAGRAPH SEPARATOR
                        + "\\u202F" // NARROW NO-BREAK SPACE
                        + "\\u205F" // MEDIUM MATHEMATICAL SPACE
                        + "\\u3000" // IDEOGRAPHIC SPACE
                        ;        
/* A \s that actually works for Java’s native character set: Unicode */
String     whitespace_charclass = "["  + whitespace_chars + "]";    
/* A \S that actually works for  Java’s native character set: Unicode */
String not_whitespace_charclass = "[^" + whitespace_chars + "]";

现在你可以在replaceAll中使用whitespace_charclass + "+"作为模式。


很抱歉,Java的正则表达式在其本地字符集上表现不佳,因此您确实需要通过奇特的方法使它们起作用。

如果你觉得空格很烦人,你应该看看如何让\w\b最终正常工作!

是的,这是可能的,但它真的很混乱。即使这样说也是太客气了。在Java中获得符合标准的正则表达式库的最简单方法是JNI到ICU的内容。这就是Google为Android所做的,因为OraSun的做法不够好。

如果您不想这样做,但仍然想坚持使用Java,我有一个前端正则表达式重写库,我编写了它来“修复”Java的模式,至少使它们符合UTS#18中RL1.2a的要求,即Unicode 正则表达式的要求。


8
这真的很古老。使用UNICODE_CHARACTER_CLASS标志(或使用(?U))在Java7中是否已修复了这个问题? - kritzikratzi
2
重写\s的更短方式是[\s\u0085\p{Z}] - Robert Tupelo-Schneck
7
@tchrist 如果这在Java 7+中已经修复,您能否更新答案并提供现在正确的方法? - beerbajay
14
在Java 7及以上版本中,您可以使用"(?U)\s"来运行符合Unicode技术标准的正则表达式,或者在创建模式时将UNICODE_CHARACTER_CLASS标志设置为true。这是相关文档链接:https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html#UNICODE_CHARACTER_CLASS - Didier A.
3
以上代码缺少 \u200B(零宽空格)。 - Jelle van Geuns
显示剩余5条评论

46

是的,你需要获取matcher.replaceAll()的结果:

String result = matcher.replaceAll(" ");
System.out.println(result);

19
我感觉自己像地球上最傻的人一样。我和另外两个人都没注意到那个错误。有时候最愚蠢的小错误也会让我们犯糊涂,是吧? - user372743
太对了!我想这种情况发生在最好的人身上。 - saibharath
如果文本中有空格,我需要获取什么? - Gilberto Ibarra
1
根据我下面的回答,如果您想匹配Unicode空格,请使用\p{Zs}而不是\s。 - Robert

18

对于Java(不是php,不是javascript,也不是其他任何语言):

txt.replaceAll("\\p{javaSpaceChar}{2,}"," ")

字符串是不可变的,因此你必须将结果赋给某个变量,比如 'txt = txt.replaceAll()'。我没有对你的回答进行投票,但可能是其他人这样做的原因。 - Enwired
8
我知道replaceAll返回一个字符串,对于Java程序员来说重要的是\p{javaSpaceChar}。 - surfealokesea
2
原始问题犯了一个错误,没有将新字符串分配给变量。指出这个错误因此是答案中最重要的点。 - Enwired
这完全解决了我的Groovy问题!终于!我一直在尝试使用所有正则表达式,包括匹配所有空格(包括非断空格ASCII 160)的正则表达式!!! - Piko

11

自这个问题被提出以来,Java已经发生了很大的变化。现在可以使用\p{Zs}组匹配各种Unicode空格字符。

因此,如果你想用一个普通的空格替换一个或多个异国情调的空格,你可以这样做:

String txt = "whatever my string is";
String newTxt = txt.replaceAll("\\p{Zs}+", " ");

除此之外,如果你使用了trim()字符串函数,你应该看一下相对较新的strip()stripLeading()stripTrailing()字符串函数。它们可以帮助你去除各种奇怪的空白字符。有关包含哪些空格的更多信息,请参见Java的Character.isWhitespace()函数。


提醒一下,这个不匹配换行符,但是这个可以。 - Captain Man
1
@CaptainMan,你所提到的答案中遗漏了JavaDoc中的一个小注释:“指定此标志可能会带来性能损失。”为了避免这种性能损失,我建议使用\p{Zl}作为行分隔符,\p{Zp}作为段落分隔符。 - Robert
1
将以下代码:txt.replaceAll("(\\p{Zs}|\\p{Zl}|\\p{Zp})+", " "); 扩展为用单个空格字符替换所有分隔符。 - Robert

6
当我向Regexbuddy(正则表达式开发应用程序)论坛发送一个问题时,我得到了更精确的答复,以回答关于\s Java问题的疑问:
“消息作者:Jan Goyvaerts
在Java中,\s、\d和\w缩写只包括ASCII字符。…这不是Java的错误,而只是在使用正则表达式时需要注意的许多事情之一。为了匹配所有Unicode空格以及换行符,您可以在Java中使用[\s\p{Z}]。RegexBuddy还不支持特定于Java的属性,如\p{javaSpaceChar}(它与[\s\p{Z}]完全匹配相同的字符)。
…\s\s将匹配两个空格,如果输入仅限于ASCII。真正的问题在于OP的代码,就像在那个问题中被接受的答案指出的那样。”

4
[\s\p{z}] 排除了 Unicode 中的 "next line" 字符 U+0085。请使用 [\s\u0085\p{Z}] - Robert Tupelo-Schneck

5

对我来说似乎有效:

String s = "  a   b      c";
System.out.println("\""  + s.replaceAll("\\s\\s", " ") + "\"");

将打印:

" a  b   c"

我认为您打算使用以下代码而不是您的代码:

我认为您打算使用以下代码而不是您的代码:

Pattern whitespace = Pattern.compile("\\s\\s");
Matcher matcher = whitespace.matcher(s);
String result = "";
if (matcher.find()) {
    result = matcher.replaceAll(" ");
}

System.out.println(result);

4

您可以使用以下代码片段实现您的目的:

import org.apache.commons.lang3.StringUtils;

StringUtils.normalizeSpace(string);

这将规范间距为单个,并将删除开头和结尾的空格。
String sampleString = "Hello    world!";
sampleString.replaceAll("\\s{2}", " "); // replaces exactly two consecutive spaces
sampleString.replaceAll("\\s{2,}", " "); // replaces two or more consecutive white spaces

4
要匹配任何空白字符,您可以使用:


Pattern whitespace = Pattern.compile("\\s", Pattern.UNICODE_CHARACTER_CLASS);

我会尽力帮助您翻译中文。以下是您需要翻译的内容:

Pattern.UNICODE_CHARACTER_CLASS选项“启用了预定义字符类和POSIX字符类的Unicode版本”,这些字符类“符合Unicode技术标准#18:Unicode正则表达式附录C:兼容属性”的规范。

相同的行为也可以通过(?U)嵌入式标志表达式来启用。例如,如果您想在Java中使用正则表达式替换/删除所有Unicode空格,则可以使用

String result = text.replaceAll("(?U)\\s+", ""); // removes all whitespaces
String result = text.replaceAll("(?U)\\s", "-"); // replaces each single whitespace with -
String result = text.replaceAll("(?U)\\s+", "-"); // replaces chunks of one or more consecutive whitespaces with a single -
String result = text.replaceAll("(?U)\\G\\s", "-"); // replaces each single whitespace at the start of string with -

查看Java在线演示

String text = "\u00A0 \u00A0\tStart reading\u00A0here..."; // \u00A0 - non-breaking space
System.out.println("Text: '" + text + "'"); // => Text: '       Start reading here...'
System.out.println(text.replaceAll("(?U)\\s+", "")); // => Startreadinghere...
System.out.println(text.replaceAll("(?U)\\s", "-")); // => ----Start-reading-here...
System.out.println(text.replaceAll("(?U)\\s+", "-")); // => -Start-reading-here...
System.out.println(text.replaceAll("(?U)\\G\\s", "-")); // => ----Start reading here... 

3
Pattern whitespace = Pattern.compile("\\s\\s");
matcher = whitespace.matcher(modLine);

boolean flag = true;
while(flag)
{
 //Update your original search text with the result of the replace
 modLine = matcher.replaceAll(" ");
 //reset matcher to look at this "new" text
 matcher = whitespace.matcher(modLine);
 //search again ... and if no match , set flag to false to exit, else run again
 if(!matcher.find())
 {
 flag = false;
 }
}

3
Mike,感谢您抽出时间回答问题,但是这个问题已经在几个月前解决了。没有必要回答这么旧的问题。 - user372743
9
如果有人能够展示一种不同、更好的解决方案,回答旧问题也是完全合理的。 - james.garriss

0

你可以使用更简单的方式:

String out = in.replaceAll(" {2}", " ");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接