Подбирайте пробелы, но не переводы строк

277

Иногда я хочу найти пробел, но не перевод строки.

До сих пор я прибегал к [ \t]. Есть ли менее неловкий путь?

JoelFan
источник
4
Кстати, эти символы также «пробел»: [\r\f].
Евгений Ярмаш
2
@eugeney кто-нибудь все еще делает фиды? (\ f's)
Аран Малхолланд
1
@AranMulholland: Любой, у кого есть характерно-ориентированный принтер. Большинство принтеров имеют символьный режим, а также PostScript или любой другой интерфейс, который называется Hewlett Packard, и для отправки страницы вы отправляете ленту новостей.
Бородин,
1
@Borodin Hewlett Packard's называется PCL (язык управления принтером).
CB_Ron

Ответы:

182

Perl версии 5.10 и более поздних версий поддерживает вспомогательные классы по вертикали и горизонтали характер, \vи \h, а также общий пробельные класс символов\s

Самое чистое решение - использовать класс символов горизонтальных пробелов\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

Вертикальное пространство шаблон \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

Есть семь вертикальных пробельных символов, которые соответствуют, \vи восемнадцать горизонтальных, которые соответствуют \h. \sсоответствует двадцать три символа

Все пробельные символы являются вертикальными или горизонтальными без перекрытия, но они не являются правильными подмножествами, поскольку \hтакже соответствуют U + 00A0 NO-BREAK SPACE, а \vтакже соответствуют U + 0085 NEXT LINE, ни один из которых не соответствует\s

Бородин
источник
7
\hработает только на тех языках, которые поддерживает PCRE.
Авинаш Радж
14
@AvinashRaj: Этот вопрос касается Perl, который, безусловно, поддерживает PCRE
Бородин
2
@AvinashRaj: За исключением того, что [[:blank:]]это не соответствует пространству без перерывов -  или"\xA0"
Бородин
6
Хочу упомянуть, что это \hпрекрасно работает для моего варианта использования, который выполнял поиск / замену в Notepad ++ на 1 или более смежных пробелах, не начинающихся с новой строки. Ничто другое (простое) не сработало.
Squidbe
8
Что делает Perl \hслегка нестандартным, так это его включение MONGOLIAN VOWEL SEPARATOR. Юникод не считает это пробелом. По этой причине Perl \hотличается от POSIX blank( [[:blank:]]в Perl, \p{Blank}в Java) и Java 8 \h. Правда, это крайний случай.
Александр Дубинский
363

Используйте двойной негатив:

/[^\S\r\n]/

То есть не-не-пробел (заглавная S дополняет) или не-возврат каретки или не-перевод строки. Распределение внешнего не ( т.е. дополнения ^в классе символов) по закону Де Моргана , это эквивалентно «пробелу, но не возврату каретки или переводу строки». Включение обоих \rи \nв шаблон корректно обрабатывает все соглашения Unix (LF), классической Mac OS (CR) и DOS-ish (CR LF) .

Не нужно верить мне на слово:

#! /usr/bin/env perl

use strict;
use warnings;

use 5.005;  # for qr//

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

Вывод:

"" => соответствовать
"\ f" => соответствовать
"\ t" => соответствовать
"\ r" => нет совпадений
"\ n" => нет совпадений

Обратите внимание на исключение вертикальной вкладки, но это рассматривается в v5.18 .

Прежде чем возражать слишком жестко, в документации Perl используется та же техника. Сноска в разделе «Пробелы» в perlrecharclass гласит:

До Perl v5.18 \sне соответствовал вертикальной табуляции. [^\S\cK](неясно) соответствует тому, что \sтрадиционно делали.

В том же разделе perlrecharclass также предлагаются другие подходы, которые не оскорбят противодействия учителей языка двойным негативам.

Вне локали и правил Unicode или когда действует /aпереключатель, « \sсоответствует [\t\n\f\r ]и, начиная с Perl v5.18, вертикальная вкладка \cK». Откажитесь \rи \nоставьте /[\t\f\cK ]/для соответствующего пробела, но не перевод строки.

Если ваш текст - Unicode, используйте код, подобный приведенному ниже, для создания шаблона из таблицы в вышеупомянутом разделе документации .

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

Другие приложения

Двойной отрицательный трюк также удобен для сопоставления буквенных символов. Помните , что \wматчи «символы, слова» буквенные символы и цифры и подчеркивания. Мы, некрасивые американцы, иногда хотим написать это, скажем,

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

но дважды отрицательный символьный класс может уважать локаль:

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

Выражение «символ слова, но не цифра или подчеркивание» таким образом немного непрозрачно. Символьный класс POSIX сообщает намерение более напрямую

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

или со свойством Unicode как предложено szbalint

if (/\p{Letter}+/) { ... }
Грег Бэкон
источник
4
Умно, но поведение очень удивительно, и я не вижу, как это менее неловко.
Qwertie
7
@Qwertie: что удивительного? Менее неловко, чем что?
ysth
9
Отлично ужасно.
9
Это очень хорошо. В соответствии с запросом вы сопоставляете пробелы (не только некоторые пробельные символы) и исключаете символ перевода строки. Ваше решение не касается вопроса: «какие пробельные символы существуют», как это не должно быть. Это именно то, что я искал. (Как было отмечено @Rory, а «перевод строки» может также включать в себя \r, например , на Windows, так считают exluding те от матча , а также: /[^\S\r\n]/)
Timo
1
Это, безусловно, удовлетворит потребности ОП и практически всех, кто занимается поиском этого вопроса (во всяком случае, на английском языке). Но это все еще плохой ответ. Там просто нет оправдания для использования этого решения, когда \hдоступно.
Алан Мур
50

Вариант ответа Грега, который также включает возврат каретки:

/[^\S\r\n]/

Это регулярное выражение безопаснее, чем /[^\S\n]/без \r. Я рассуждаю так: Windows использует \r\nновые строки, а Mac OS 9 использует \r. Вы вряд ли найдете \rбез нас в \nнаше время, но если вы найдете это, это не может означать ничего, кроме новой строки. Таким образом, поскольку \rможет означать новую строку, мы должны исключить ее тоже.

Рори О'Кейн
источник
1
+1 Решение Грега испортило мой текст, твое сработало нормально.
Тимо Хуовинен
Вы можете быть удивлены тем, сколько программ по-прежнему используют «\ r» для окончания строк. Иногда мне требовалось время, чтобы понять, что моя проблема в том, что файл использовал их. Или что он использовал кодировку символов MacRoman ...
mivk
2
Похоже, @Greg сначала «неправильно» изменил его и не кредитовал вас. Вот почему я здесь голосую.
Андре Элрико
14

Приведенное ниже регулярное выражение будет соответствовать пробелам, но не символу новой строки.

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

DEMO

Если вы хотите добавить возврат каретки и затем добавить \rс |оператором внутри отрицательного предпросмотра.

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

DEMO

Добавьте +после группы без захвата, чтобы соответствовать один или несколько пробелов.

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

DEMO

Я не знаю, почему вы, люди, не упомянули класс символов POSIX, [[:blank:]]который соответствует любым горизонтальным пробелам ( пробелы и табуляции ). Этот класс POSIX chracter будет работать с BRE ( базовые регулярные выражения ), ERE ( расширенное регулярное выражение ), PCRE ( регулярное выражение, совместимое с Perl ).

DEMO

Авинаш Радж
источник
Это лучшее решение!
Лоретопариси
13

То, что вы ищете, это blankкласс символов POSIX . В Perl это упоминается как:

[[:blank:]]

в Java (не забудьте включить UNICODE_CHARACTER_CLASS):

\p{Blank}

По сравнению с аналогом \h, POSIX blankподдерживается еще несколькими движками регулярных выражений ( ссылка ). Основным преимуществом является то, что его определение зафиксировано в Приложении C: Свойства совместимости регулярных выражений Unicode и стандарт для всех разновидностей регулярных выражений, поддерживающих Unicode. (Например, \hв Perl выбирается дополнительное включение MONGOLIAN VOWEL SEPARATOR.) Однако аргумент в пользу этого \hзаключается в том, что он всегда обнаруживает символы Unicode (даже если движки не согласны с какими), в то время как классы символов POSIX часто по умолчанию ASCII. только (как в Java).

Но проблема в том, что даже придерживание Unicode не решает проблему на 100%. Рассмотрим следующие символы, которые не считаются пробелами в Юникоде:

  • U + 180E монгольский разделитель гласных

  • U + 200B НУЛЕВОЕ ПРОСТРАНСТВО

  • U + 200C с нулевой шириной без соединения

  • U + 200D ZERO ШИРОКИЙ СОЕДИНИТЕЛЬ

  • U + 2060 WORD JOINER

  • U + FEFF с нулевой шириной неразрывного пространства

    Взято с https://en.wikipedia.org/wiki/White-space_character

Вышеупомянутый монгольский разделитель гласных не включен, что, вероятно, является веской причиной. Это, наряду с 200C и 200D, происходит в словах (AFAIK), и поэтому нарушает кардинальное правило, которому подчиняются все остальные пробелы: вы можете токенизировать его. Они больше похожи на модификаторы. Однако ZERO WIDTH SPACE, WORD JOINERи ZERO WIDTH NON-BREAKING SPACE(если он используется в качестве кроме отметки порядка байтов) подходит правило пробела в моей книге. Поэтому я включаю их в свой класс горизонтальных пробельных символов.

В Java:

static public final String HORIZONTAL_WHITESPACE = "[\\p{Blank}\\u200B\\u2060\\uFFEF]"
Александр Дубинский
источник
Вам необходимо добавить соответствующие флаги компиляции regexp в компиляцию Java и использовать Java 7 или более позднюю версию. В любом случае, вопрос был не о Java или PCRE вообще, так что все это несущественно.
трилист
@tchrist Спасибо, что указали на это. Я обновлю свой ответ. Я не согласен, однако, что мой ответ не имеет значения. Что является несущественным, так это perlтег в оригинальном вопросе.
Александр Дубинский
1
@AleksandrDubinsky, \ p {Blank} не поддерживается в JavaScript, поэтому определенно не «стандарт для всех разновидностей регулярных выражений» -1
Валентин Васильев
Наиболее информативно. Я нахожу тревожным знать, что общий и полный класс символов «горизонтальный пробел» не существует, и что [\p{Blank}\u200b\u180e]требуются ужасы вроде . Правда, имеет смысл, что разделитель гласных не считается пробельным символом, но почему пробел нулевой ширины не в таких классах, как \sи \p{Blank}, лучше меня.
Тимо
Продолжение: я прочитал, что оба считаются «нейтральными по отношению к границе», хотя это не объясняет почему .
Тимо
-4

m/ /gпросто дайте место / /, и это будет работать. Или используйте \S- он заменит все специальные символы, такие как табуляция, новые строки, пробелы и так далее.

saiprathapreddy.obula
источник