Білий простір, що відповідає Regex - Java


106

Java API для регулярних виразів констатує, що \sбуде відповідати пробілу. Отже, регулярний вираз \\s\\sповинен відповідати двом пробілам.

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

Мета цього - замінити всі екземпляри двох послідовних пробілів на один пробіл. Однак насправді це не працює.

У мене є серйозне непорозуміння виразів або терміна "пробіл"?


1
У рядку є функція substituAll, яка заощадить вам кілька рядків коду. 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 +". Цим ви замінюєте два чи більше пробілів на один.
хрзафер

Я вже більше години цікавлюся, чому мій спліт не розщеплюється на пробіл. Завдяки мільйонів!
Марцін

Відповіді:


44

Так, вам потрібно взяти результат matcher.replaceAll():

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

18
Гах. Я відчуваю себе найбільшим ідіотом на землі. Ні я, ні двоє інших людей, схоже, цього не помічали. Я думаю, що найглупіші маленькі помилки іноді нас відкидають, так?

Такий справжній! Я здогадуюсь, що це трапляється з найкращим з них
Сайбхарат

Що трапиться, якщо мені потрібно отримати, якщо в тексті були білі пробіли.?
Гілберто Ібарра

На мою відповідь нижче, використовуйте \ p {Zs} замість \ s, якщо ви хочете відповідати пробілу unicode.
Роберт

194

Ви не можете використовувати \sв Java для узгодження білого простору на власному наборі символів, тому що Java не підтримує властивість білого простору Unicode - навіть якщо це робити суворо потрібно, щоб відповідати RL1.2 UTS # 18! На жаль, це не відповідає стандартам.

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, Регулярні вирази Unicode .


12
Дякуємо, що голова вирішила щодо обмежень зворотного виразів Java. +1
рейгернер

4
Я пішов проголосувати цю відповідь як корисну і виявив, що вже є. Тож дякую вдруге :)
Ендрю Уайльд

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,}"," ")

Рядки незмінні, тому вам доведеться присвоїти результат чомусь, наприклад, 'txt = txt.replaceAll ()' Я не проголосував вашу відповідь, але це може бути причиною того, щоб це зробив хтось інший.
Переглянув

6
Я знаю, що substituAll повертає рядок, важлива річ 4 програми java - \\ p {javaSpaceChar}
surfealokesea

2
Оригінальне запитання допустило помилку, не присвоївши нову рядок змінній. Вказуючи, що помилка є, таким чином, найважливішим моментом відповіді.
Запропоновано

Це повністю вирішило мою проблему в Groovy! Нарешті! Спробувавши кожен регекс, я міг би знайти, що він би відповідав усім пробілам, включаючи NON-BREAK-SPACE (ASCII 160) !!!
Піко

5

коли я надіслав запитання на форум Regexbuddy (програма для розробників regex), я отримав більш точну відповідь на моє питання щодо Java:

"Автор повідомлення: Ян Гойварц

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

... \ s \ s буде відповідати двом пробілам, якщо вхід є лише ASCII. Справжня проблема полягає в коді ОП, на що вказує прийнята відповідь у цьому питанні ".


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()string, вам слід поглянути на (відносно нову) strip(),stripLeading() і stripTrailing()функцію по струнах. Можна допомогти вам обрізати всілякі символи білого простору. Для отримання додаткової інформації про те, який простір включено, див. Character.isWhitespace()Функцію Java .


-3

Використання пробілів в РЕ - це біль, але я вважаю, що вони працюють. Проблему ОП також можна вирішити за допомогою StringTokenizer або методу split (). Однак, для використання RE (не коментуйте println () для перегляду того, як Matcher розбиває String), ось зразок коду:

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
WTF !? Чому б ви хотіли робити все це, коли можете просто зателефонувати replaceAll()замість цього?
Алан Мур
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.