Соответствие регулярным выражениям пробелов - Java - PullRequest
93 голосов
/ 19 января 2011

Java API для регулярных выражений утверждает, что \s будет соответствовать пробелу. Таким образом, регулярное выражение \\s\\s должно соответствовать двум пробелам.

Pattern whitespace = Pattern.compile("\\s\\s");
matcher = whitespace.matcher(modLine);
while (matcher.find()) matcher.replaceAll(" ");

Целью этого является замена всех экземпляров двух последовательных пробелов одним пробелом. Однако на самом деле это не работает.

Имею ли я серьезное недопонимание регулярных выражений или термина "пробел"?

Ответы [ 8 ]

173 голосов
/ 19 января 2011

Вы не можете использовать \s в Java для сопоставления пробелов в своем собственном наборе символов, потому что Java не поддерживает свойство пробела Unicode - хотя это строго необходимо для соответствия UTS #RL1.2! 18-х годов, к сожалению, не соответствует стандартам.

Unicode определяет 26 кодовых точек, так как \p{White_Space}: 20 из них являются различными типами \pZ GeneralCategory= Разделитель , а остальные 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 + "]";

Теперь вы можете использовать whitespace_charclass + "+" в качестве шаблона в вашем replaceAll.


Извините за все это. Регулярные выражения Java просто не очень хорошо работают с собственным собственным набором символов, , и поэтому вам действительно нужно прыгать через экзотические обручи, чтобы заставить их работать.

И если вы думаете, что пробел плохой, вы должны увидеть, что вы должны сделать, чтобы получить \w и \b, чтобы наконец вести себя правильно!

Да, это возможно, и да, это безумный беспорядок.Это даже благотворительность.Самый простой способ получить стандартизированную библиотеку регулярных выражений для Java - это использовать JNI для ICU.Это то, что Google делает для Android, потому что OraSun не соответствует.

Если вы не хотите этого делать, но все еще хотите придерживаться Java, у меня есть библиотека переписывания regex для внешнего интерфейса, которую я написал,«Исправляет» шаблоны Java, по крайней мере, для их соответствия требованиям RL1.2a в UTS # 18, Регулярные выражения Unicode .

42 голосов
/ 19 января 2011

Да, вам нужно получить результат matcher.replaceAll ():

String result = matcher.replaceAll(" ");
System.out.println(result);
12 голосов
/ 11 июня 2013

Для Java (не php, не javascript, не anyother):

txt.replaceAll("\\p{javaSpaceChar}{2,}"," ")
5 голосов
/ 03 ноября 2014

Когда я отправил вопрос на форум Regexbuddy (Regex Developer Application), я получил более точный ответ на мой вопрос Java:

"Автор сообщения: Ян Гойваэртс

В Java, сокращения \ s, \ d и \ w включают только символы ASCII. ... Это не ошибка в Java, а просто одна из многих вещей, которые необходимо учитывать при работе с регулярными выражениями. Чтобы соответствовать всемПробелы в Юникоде и разрывы строк можно использовать в Java [\ s \ p {Z}]. RegexBuddy пока не поддерживает специфичные для Java свойства, такие как \ p {javaSpaceChar} (что соответствует точно тем же символам, что и \\ p {Z}]).

... \ s \ s будет соответствовать двум пробелам, если вход только ASCII. Настоящая проблема заключается в коде OP, на что указывает принятый ответв этом вопросе. "

4 голосов
/ 19 января 2011

Кажется, работает для меня:

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);
1 голос
/ 18 мая 2018

Для вашей цели вы можете использовать этот сннипет:

import org.apache.commons.lang3.StringUtils;
StrintUtils.StringUtils.normalizeSpace(string);

это нормализует интервал до одиночного и удаляет также начальные и конечные пробелы.

Для вашей цели вы можете использовать этот сннипет:

import org.apache.commons.lang3.StringUtils;
StrintUtils.StringUtils.normalizeSpace(string);

это нормализует интервал до одиночного и удалит также начальные и конечные пробелы.

String sampleString = "Привет, мир!"; sampleString.replaceAll ("\ s {2}", ""); // заменяет ровно два последовательных пробела

sampleString.replaceAll ("\ s {2,}", ""); // заменяет два или более последовательных пробела

1 голос
/ 15 сентября 2011
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;
 }
}
0 голосов
/ 19 января 2011

Использование пробелов в RE - это боль, но я считаю, что они работают.Проблему ОП также можно решить с помощью метода StringTokenizer или метода split ().Тем не менее, чтобы использовать RE (раскомментируйте println (), чтобы увидеть, как сопоставитель разбивает строку), вот пример кода:

import java.util.regex.*;

public class Two21WS {
    private String  str = "";
    private Pattern pattern = Pattern.compile ("\\s{2,}");  // multiple spaces

    public Two21WS (String s) {
            StringBuffer sb = new StringBuffer();
            Matcher matcher = pattern.matcher (s);
            int startNext = 0;
            while (matcher.find (startNext)) {
                    if (startNext == 0)
                            sb.append (s.substring (0, matcher.start()));
                    else
                            sb.append (s.substring (startNext, matcher.start()));
                    sb.append (" ");
                    startNext = matcher.end();
                    //System.out.println ("Start, end = " + matcher.start()+", "+matcher.end() +
                    //                      ", sb: \"" + sb.toString() + "\"");
            }
            sb.append (s.substring (startNext));
            str = sb.toString();
    }

    public String toString () {
            return str;
    }

    public static void main (String[] args) {
            String tester = " a    b      cdef     gh  ij   kl";
            System.out.println ("Initial: \"" + tester + "\"");
            System.out.println ("Two21WS: \"" + new Two21WS(tester) + "\"");
}}

Он производит следующее (скомпилируйте с javac и запустите накомандная строка):

% java Two21WS По умолчанию: "ab cdef gh ij kl" Two21WS: "ab cdef gh ij kl"

...