Как использовать preg_replace для удаления лишних одиночных пробелов

Мы извлекаем текст из файлов PDF, и очень часто результаты содержат искаженный текст. В частности, добавление пробелов между символами слова. например SEATTLE возвращается как S E A T T L E.

Есть ли выражение RegEx для preg_replace, которое может удалить любые пробелы в случае n количества односимвольных «слов»? В частности, удалить пробелы из любого вхождения строки, состоящей из более чем трех одиночных буквенных символов и разделенных пробелами?

Если погуглил какое-то время, но не могу даже представлять себе, как построить выражение. Как указано в комментарии, я не хочу, чтобы ВСЕ пробелы удалялись, но только тогда, когда встречается> 3 одиночных альфа-символа, например. Welcome to the Greater S E A T T L E area должен стать Welcome to the Greater SEATTLE area. Результат будет использоваться при полнотекстовом поиске, поэтому чувствительность к регистру не является проблемой.

Вы должны использовать простой подход с preg_replace_callback. Сопоставьте пробелы '~\b[A-Za-z](?: [A-Za-z]){2,}\b~' и str_replace в анонимной функции.

Wiktor Stribiżew 03.10.2018 19:05

Это выглядит многообещающе, но, как вы можете догадаться, RegEx для меня - это волшебство. Можете ли вы предоставить это выражение в качестве ответа в рабочем примере?

GDP 03.10.2018 19:07
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
3
2
118
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Вы можете использовать простой подход с preg_replace_callback. Сопоставьте пробелы '~\b[A-Za-z](?: [A-Za-z]){2,}\b~' и str_replace в анонимной функции:

$regex = '~\b[A-Za-z](?: [A-Za-z]){2,}\b~';
$result = preg_replace_callback($regex, function($m) {
     return str_replace(" ", "", $m[0]);
}, $s);

См. демонстрация регулярного выражения.

Чтобы соответствовать только последовательностям заглавных букв, удалите a-z из шаблона:

$regex = '~\b[A-Z](?: [A-Z]){2,}\b~';

И еще: могут быть мягкие / жесткие пробелы, вкладки и другие пробелы. Затем используйте

$regex = '~\b[A-Za-z](?:\h[A-Za-z]){2,}\b~u';
                        ^^                ^

Наконец, чтобы соответствовать любой букве Unicode, используйте \p{L} (чтобы соответствовать только прописным буквам, \p{Lu}) вместо [a-zA-Z]:

$regex = '~\b\p{L}(?:\h\p{L}){2,}\b~u';

ПРИМЕЧАНИЕ: в некоторых случаях, скорее всего, не сработает, например. когда есть однобуквенные слова. Вам придется обрабатывать эти случаи отдельно / вручную. В любом случае, не существует безопасного способа исправить проблемы с распознаванием текста только с помощью регулярных выражений.

Детали выкройки

  • \b - граница слова
  • [A-Za-z] - одна буква
  • (?: [A-Za-z]){2,} - 2 или более появления
    • - пробел (\h соответствует любым горизонтальным пробелам)
    • [A-Za-z] - одна буква
  • \b - граница слова

Когда используется модификатор u, \h поддерживает Unicode.

СПАСИБО ... Ваш комментарий заработал после того, как удалил ~ на Regtest101.com. Заменит ли это все вхождения? RegTester останавливается при первом появлении.

GDP 03.10.2018 19:16

Так случилось, что я ждал длинного сценария CLI, работающего с моим отладчиком, поэтому тем временем проводил исследования.

GDP 03.10.2018 19:22

@GDP Как видите, во всех других ответах используются гораздо более сложные регулярные выражения. Вот что я имел в виду, говоря «простой подход». Я также думал о регулярном выражении на основе \G, но когда у вас есть вся мощь языка PHP, этот подход является лучшим, IMHO, поскольку шаблон очень удобочитаем и легко улучшается, если вам нужно избегать определенных контекстов.

Wiktor Stribiżew 03.10.2018 20:12

Вы можете использовать этот подход чистого регулярного выражения с поисковыми запросами и \G:

$re = '~\b(?:(?=(?:\pL\h+){3}\pL\b)|(?<!^)\G)(\pL)\h+(?=\pL\b)~';

$repl = preg_replace($re, '$1', $str);

Демо RegEx

Детали RegEx:

  • \b: граница слова соответствия
  • (?:: Запустить группу без захвата
    • (?=(?:\pL\h+){3}\pL\b): Загляните вперед, чтобы убедиться, что у нас есть 3+ отдельных буквы, разделенных 1+ пробелами
    • |: ИЛИ
    • (?<!^)\G: \G утверждает позицию в конце предыдущего совпадения. (?<!^) гарантирует, что мы не совпадаем с началом строки для первого совпадения
  • ): конец группы без захвата
  • (\pL): сопоставьте одну букву и запишите ее
  • \h+: за ним следует 1+ горизонтальных пробелов
  • (?=\pL\b): Утверждаю, что впереди всего одна буква
  • В замена мы используем $1, который представляет собой букву слева от пробела, которую мы фиксируем.

Кажется, это тоже работает, но я должен принять другой ответ, поскольку он ответил первым, помог мне начать в правильном направлении, но СПАСИБО!

GDP 03.10.2018 19:27

Вы можете сделать это за один раз:

(?i:(?<!\S)([a-z]) +((?1))|\G(?!\A) +((?1))\b)

См. живая демонстрация здесь

Объяснение:

(?i: # Start of non-capturing group with case-insensitive modifier on
    (?<!\S) # Negative lookbehind to ensure there is no leading non-whitespace character
    ([a-z]) + # Capture one letter and at least one space
    ((?1)) # Capture one letter in 2nd capturing group
    | # Or
    \G(?!\A) + # Start match from where previous match ends 
               # with matching spaces
    ((?1))\b # Match a letter at word boundary
) # End of non-capturing group

Код PHP:

$str = preg_replace('~(?i:(?<!\S)([a-z]) +((?1))|\G(?!\A) +((?1))\b)~', '$1$2$3', $str);

Кажется, это тоже работает, но я должен принять другой ответ, поскольку он ответил первым, помог мне начать в правильном направлении, но СПАСИБО!

GDP 03.10.2018 19:27

Другие вопросы по теме