Хочу поделиться компактной функцией для очищения лишних, повторяющихся пробелов и пробельных символов в строках. Не считайте это призывом, но если можно привести строковые данные в красивый вид без лишних хлопот, то почему бы и не воспользоваться. Те, кто не знаком с регулярными выражениями (regular expressions, RegExp, regex), может приоткроет форточку в этот славный и замороченный мир (Регулярные выражения (regexp) — основы)

Начнём издалека. Excel СЖПРОБЕЛЫ()

Есть функция в Excel СЖПРОБЕЛЫ(), цитирую Excel, Функция СЖПРОБЕЛЫ, support.microsoft.com

Удаляет из текста все пробелы, за исключением одиночных пробелов между словами. Функция СЖПРОБЕЛЫ используется для обработки текстов, полученных из других прикладных программ, если эти тексты могут содержать лишние пробелы.

Иными словами был текст, наполненный пробелами "  Съешь ещё этих мягких французских булок,  да выпей чаю  " А функция СЖПРОБЕЛЫ() вернёт нормальный текст без пробелов в конце и начале строки, а все двойные, тройные и прочие пробелы между словами преобразуются в один единственный пробел: "Съешь ещё этих мягких французских булок, да выпей чаю".

Моя личная предыстория

Т.к. в свою бытность часто приходилось контактировать с пользовательскими данными от самих пользователей (т.е. какого-либо id в выгрузках в большинстве случаев не существовало), то приходилось ВПР-ить (иначе говоря: JOIN-ить, смапить (лично слышал много раз, за правильность применения термина не ручаюсь)) строчные значения от тех же или иных пользователей из другой выгрузки и первичная очистка строки для ВПР() при помощи СЖПРОБЕЛЫ() просто превратилась в неотъемлемую процедуру.

Хоть функция СЖПРОБЕЛЫ() крайне облегчало жизнь и уменьшало потерю не найденных записей, однако не редко в выгрузках по >100k строк встречались строковые записи с пробельным символом (например: табуляция, неразрывный пробел), которые не обрабатывались функцией СЖПРОБЕЛЫ(). В версиях Excel по 2016 пробельные символы пропускались, про 2019 не могу ничего конкретного сказать, в текущей версии 365 пробельные символы вычищаются. Соответственно, все встречаемые пробельные символы нужно было предварительно обрабатывать вручную.

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

Источник проблемы

  1. Пользователь может сам вручную заполнить текст и в процессе заполнения случайно вбить 2 лишних пробела;

  2. Однако, не редко текст для заполнения с пробельными символами уже где-то написан (в Word-е, интернете и пр.) и его просто берут и копируют.

Собственно решение - очищаем

Используем регулярные выражения wikipedia, Регулярные выражения

  1. Все пробельные (в том числе повторяющиеся) символы (\s+) заменяем на пробелы;

  2. Очищаем начало и конец строки от пробелов .strip() / .trim()

Python
import re

def purge(str_in: str) -> str:
    """
    Замена всех пробельных (повторяющихся) символов в строке на единичные пробелы
    и очистка строки от пробелов в конце и в начале
    :param str_in: строка на обработку
    :return: обработанная строка
    """
    if isinstance(str_in, str):
        return re.sub(r"\s+", " ", str_in).strip()
JavaScript
function purge(str_in) {
  // Замена всех пробельных (и повторяющихся) символов в строке на единичные пробелы
  // и очистка строки от пробелов в конце и в начале
  return str_in.replace(/\s+/g, " ").trim();
}
VBS
Function purge(str_in)
  ' Замена всех пробельных (и повторяющихся) символов в строке на единичные пробелы
  ' и очистка строки от пробелов в конце и в начале
  Set objRegExp = CreateObject("VBScript.RegExp")
  With objRegExp
    .Pattern = "\s+"
    .Multiline = True
    .Global = True
  End With
  purge = Trim(objRegExp.Replace(str_in, " "))
End Function

Вдохновлено: stackoverflow, Regex to replace multiple spaces with a single space

Escape-последовательность "\s"

Не смогу ответить за все реализации RegExp на всех языках, но судя по wikipedia, Регулярные выражения Под "\s" подразумеваются все ниже перечисленные Escape-последовательности:

Символ

Эквивалент

\f

Знак новой страницы

\n

Знак перевода строки

\r

Знак возврата каретки

\t

Знак табуляции

\v

Знак вертикальной табуляция

learn.microsoft.com, Escape-последовательности

Заключительное слово

В одном из наших проектов этот метод / функция прочно закрепилась в функционале, тем самым "защитив" от пользователей базу данных в тех местах, где от них ожидался текст размером с один абзац; т.е. (подчёркиваю) НЕ в тех случаях, когда от пользователя ожидалось "сочинение", разбитое по абзацам.

FrontEnd и BackEnd имеет свойство меняться, так что очищение в 2х местах не показалось лишним
FrontEnd и BackEnd имеет свойство меняться, так что очищение в 2х местах не показалось лишним

Спасибо за внимание!

Комментарии (16)


  1. mc2
    31.08.2025 17:19

    Код на Python повторяет код на VBS.

    На Python вроде бы как то так: re.sub(r'\s+', ' ', str)


    1. aamonster
      31.08.2025 17:19

      /g или аналог в питоне не надо разве?


      1. lfwsmrp Автор
        31.08.2025 17:19

        Нет, по умолчанию (условно) global = true, т.е. везде заменяет

        А для номеров вхождений re.sub() имеет параметр count, т.е. вот так примерно:

        re.sub(r"\s+", " ", str_in, count=1)

        https://translated.turbopages.org/proxy_u/en-ru.ru.c8cf6852-68b4889a-e1b508bf-74722d776562/https/stackoverflow.com/questions/3951660/how-to-replace-the-first-occurrence-of-a-regular-expression-in-python


    1. lfwsmrp Автор
      31.08.2025 17:19

      Виноват, исправил, спасибо


  1. aamonster
    31.08.2025 17:19

    s/\s+/ /g точно стоит написания целой статьи?

    А, пардон – ещё trim. s/^ | $//g (если именно регэкспом)


    1. lfwsmrp Автор
      31.08.2025 17:19

      Честно скажу - не знаю. Да и хотел покороче а "мыслию по древу растёкся" (

      Просто видел вот такое (и не единожды в разных интерпретациях):

      https://sky.pro/media/udalenie-lishnih-probelov-v-stroke-v-python/?ysclid=mezypmjc8s446455963

      Такое ощущение, что многие сознательно обходят тему регулярных выражений. Всё что угодно, лишь бы их не использовать, может реально кто-то не слышал...

      Скажем так: эта статья - компенсация за боль ручной вычистки )


      1. tuxi
        31.08.2025 17:19

        Такое ощущение, что многие сознательно обходят тему регулярных выражений. Всё что угодно, лишь бы их не использовать, может реально кто-то не слышал...

        Надо смотреть язык, конкретные реализации и входные данные. Так то создать обьект RegExp и скомпилировать выражение может оказаться недешово по накладным расходам.


        1. aamonster
          31.08.2025 17:19

          Да, примерно всегда. Но вы гляньте реализацию по ссылке :-)


          1. tuxi
            31.08.2025 17:19

            Ход мыслей автора того решения мне понравился) креативный человек))


      1. xSVPx
        31.08.2025 17:19

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

        Всё вместе приводит к тому, что если мы не ожидаем прям "много строк", яб написал явно "заменить все табуляции на пробелы", "заменить все переводы строки на пробелы" итд итп, а потом "заменить два пробела на один пока что-то меняется". Да - это, возможно будет медленнее, но если быстро не требуется - это не проблема.

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

        Тут как с рекурсией. Лучше поберечься. Если можно.


        1. edyatl
          31.08.2025 17:19

          На сколько понял речь идёт об обработке строки где-то до 1000 символов перед записью в бд. В таком случае Python и функция с регуляркой подходят больше всего.

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


    1. lfwsmrp Автор
      31.08.2025 17:19

      И по поводу функции .trim() - в Python это .strip() называется, но суть, не меняется удаляет начальные и конечные пробелы (и, "да", можно что-нибудь иное удалить но только на концах строки)

      https://ru.wikipedia.org/wiki/Trim


    1. sunnybear
      31.08.2025 17:19

      s/^\s+|\s+$//g


      1. aamonster
        31.08.2025 17:19

        Да, действительно, плюс/звезду забыл.


  1. alef13
    31.08.2025 17:19

    Ну и однострочник на баше, без привлечения внешних утилит:

    while IFS= read -a LINE; do echo ${LINE[*]}; done < filename.txt


    1. xotkot
      31.08.2025 17:19

      а мне нравиться использовать его(bash) как клей для стандартных unix/linux утилит

      ... | tr -s '[:space:]' ' ' | sed -e 's/^ //' -e 's/ $//'
      

      взгляд сразу цепляется за "блоки" и легко воспринимать