Регулярное выражение соответствия пробелов - Java

106

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

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

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

Я серьезно неправильно понимаю регулярные выражения или термин «пробел»?


источник
1
String имеет функцию replaceAll, которая сэкономит вам несколько строк кода. download.oracle.com/javase/1.5.0/docs/api/java/lang/String.html
Zach L,
1
Это не ваше недоразумение, а Java. Попробуйте разделить строку, "abc \xA0 def \x85 xyz"чтобы понять, что я имею в виду: там всего три поля.
tchrist
3
Вы пробовали "\\ s +". Таким образом вы заменяете два или более пробелов на один.
hrzafer 05
Я уже более часа задавался вопросом, почему мой раздел \\ не разбивается на пробелы. Бесконечно благодарен!
Марцин

Ответы:

44

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

String result = matcher.replaceAll(" ");
System.out.println(result);
Раф Левиен
источник
18
Ага. Я чувствую себя самым большим идиотом на земле. Похоже, ни я, ни двое других людей этого не заметили. Думаю, иногда нас сбивают с толку самые глупые маленькие ошибки, а?
Это точно! Думаю, это случается с лучшими из них
сайбхарат
Что произойдет, если мне понадобится получить, если в тексте будут пробелы?
Жилберто Ибарра
В моем ответе ниже используйте \ p {Zs} вместо \ s, если вы хотите сопоставить пробелы Юникода.
Роберт
195

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

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

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


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

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

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

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

Христос
источник
12
Спасибо за внимание к ограничениям регулярных выражений Java. +1
ridgerunner
4
Я пошел голосовать за этот ответ как за полезный и обнаружил, что уже получил. Так что спасибо вам второй раз :)
Andrew Wyld
5
это действительно старый. правильно ли, что это было исправлено в java7 с флагом UNICODE_CHARACTER_CLASS? (или используя (? U))
kritzikratzi
5
@tchrist Если это исправлено в java 7+, не могли бы вы обновить ответ, используя теперь правильный способ сделать это?
beerbajay
7
В Java 7+ вы можете: "(? U) \ s" запускать регулярное выражение в соответствии с техническим стандартом Unicode. Или вы можете сделать флаг UNICODE_CHARACTER_CLASS истинным при создании шаблона. Вот документ: docs.oracle.com/javase/7/docs/api/java/util/regex/…
Дидье А.
15

Для Java (не для php, не для javascript и не для других):

txt.replaceAll("\\p{javaSpaceChar}{2,}"," ")
Surfealokesea
источник
Строки неизменяемы, поэтому вы должны присвоить результат чему-то, например 'txt = txt.replaceAll ()' Я не голосовал против вашего ответа, но, возможно, поэтому кто-то другой сделал это.
Enwired
6
Я знаю, replaceAll возвращает строку, важная вещь для 4 java-программистов - это \\ p {javaSpaceChar}
surfealokesea
2
В исходном вопросе была сделана ошибка: новая строка не была присвоена переменной. Таким образом, указание на эту ошибку является наиболее важным моментом ответа.
Проводной
Это полностью решило мою проблему в Groovy! В заключение! Пробовал каждое регулярное выражение, которое смог найти, которое соответствовало бы всем пробелам, включая NON-BREAK-SPACE (ASCII 160) !!!
Piko
5

когда я отправил вопрос на форум Regexbuddy (приложение для разработчиков регулярных выражений), я получил более точный ответ на свой вопрос Java:

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

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

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

Туомас
источник
3
[\s\p{z}]опускает Unicode-символ "следующей строки" U + 0085. Используйте [\s\u0085\p{Z}].
Роберт Тупело-Шнек,
3

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

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);
Михай Тоадер
источник
3

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

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
Рашид М.В.
источник
1
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
Майк, хотя я ценю, что вы нашли время ответить, этот вопрос был решен несколько месяцев назад. Нет необходимости отвечать на такие старые вопросы.
6
Если кто-то может предложить другое, лучшее решение, отвечать на старые вопросы совершенно нормально.
james.garriss
1

Java эволюционировала с тех пор, как эта проблема была впервые поднята. Вы можете сопоставить все символы пробела Юникода, используя\p{Zs} группу.

Таким образом, если вы хотите заменить одно или несколько экзотических пространств на простое, вы можете сделать это:

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

Также стоит знать, если вы использовали trim()функцию строки , вы должны взглянуть на (относительно новый) strip(), stripLeading()и stripTrailing()функцию по струнам. Это может помочь вам обрезать всевозможные белые пробелы. Для получения дополнительной информации о том, какое пространство включено, см. Character.isWhitespace()Функцию Java .

Роберт
источник
-3

Использование пробелов в RE - проблема, но я считаю, что они работают. Проблема OP также может быть решена с помощью 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"

Манидип Сенгупта
источник
8
Какого черта !? Зачем вам все это делать, если replaceAll()вместо этого можно просто позвонить ?
Алан Мур