Все, что можно сделать с помощью модуля textwrap в Python

Узнайте обо всем, что можно сделать с помощью модуля textwrap в Python, включая форматирование, обертывание текста, обрезку и многое другое.

В Python есть множество возможностей для форматирования строк и текста, включая f-строки, функцию format(), шаблоны и многое другое. Однако есть один модуль, о котором мало кто знает, и называется он textwrap.

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

Shorten

Начнем с очень простой, но очень полезной функции из модуля textwrap, которая называется shorten:

from textwrap import shorten

shorten("This is a long text or sentence.", width=10)
# 'This [...]'
shorten("This is a long text or sentence.", width=15)
# 'This is a [...]'
shorten("This is a long text or sentence.", width=15, placeholder=" <...>")
# 'This is a <...>'

Как следует из названия, shorten позволяет обрезать текст до определенной длины (ширины), если указанная строка слишком длинная. По умолчанию в качестве вставки для обрезанного текста используется […], но это можно изменить с помощью аргумента placeholder.

Wrap

Более интересной функцией этого модуля является wrap. Очевидно, что она используется для разбиения длинного текста на строки одинаковой длины, но есть и другие возможности:

from textwrap import wrap
s = '1234567890'
wrap(s, 3)
# ['123', '456', '789', '0']

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

Однако использование этой функции имеет некоторые недостатки:

s = '12\n3  45678\t9\n0'
wrap(s, 3)
# ['12', '3  ', '456', '78', '9 0']
# the first ("12") element "includes" newline
# the 4th element ("78") "includes" tab
wrap(s, 3, drop_whitespace=False, tabsize=1)
# ['12 ', '3  ', '456', '78 ', '9 0']

При использовании wrap следует быть осторожным с пробелами – выше показано поведение с символами новой строки, табуляции и пробела. Видно, что первый элемент ( 12) “включает” новую строку, а 4-й элемент ( 78) “включает” табуляцию, однако они отбрасываются по умолчанию, поэтому эти элементы содержат только 2 символа вместо 3.

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

Это может быть очевидно, но wrap также отлично подходит для переформатирования целых файлов под определенную ширину строки:

with open("some-text.md", "r", encoding="utf-8") as f:
    formatted = wrap(f.read(), width=80)  # List of lines
    formatted = fill(f.read(), width=80)  # Single string that includes line breaks 
    # ... write it back

Мы также можем использовать функцию fill, которая является сокращением для “\n”.join(wrap(text, …)). Разница между этими двумя функциями заключается в том, что wrap даст нам список строк, которые нам нужно будет объединить самостоятельно, а fill даст нам одну строку, которая уже объединена с помощью новых строк.

TextWrapper

Модуль textwrap также включает в себя более мощную функцию обертывания версий, которая представляет собой класс TextWrapper:

import textwrap

w = textwrap.TextWrapper(width=120, placeholder=" <...>")
for s in list_of_strings:
    w.wrap(s)
    # ...

Этот класс и его метод wrap отлично подходят, если нам нужно вызвать wrap с одними и теми же параметрами несколько раз, как показано выше.

А пока мы рассматриваем TextWrapper, давайте попробуем использовать еще несколько ключевых аргументов:

user = "John"
prefix = user + ": "
width = 50
wrapper = TextWrapper(initial_indent=prefix, width=width, subsequent_indent=" " * len(prefix))
messages = ["...", "...", "..."]
for m in messages:
    print(wrapper.fill(m))

# John: Lorem Ipsum is simply dummy text of the
#       printing and typesetting industry. Lorem
# John: Ipsum has been the industry's standard dummy
#       text ever since the 1500s, when an
# John: unknown printer took a galley of type and
#       scrambled it to make a type specimen

Здесь мы видим использование initial_indent и subsequent_indent для отступа первой строки абзаца и последующих, соответственно. Есть еще несколько вариантов, которые вы можете найти в документации.

Кроме того, поскольку TextWrapper – это класс, мы можем расширить его и полностью переопределить некоторые из его методов:

from textwrap import TextWrapper

class DocumentWrapper(TextWrapper):

    def wrap(self, text):
        split_text = text.split('\n')
        lines = [line for par in split_text for line in TextWrapper.wrap(self, par)]
        return lines


text = """First line,

Another, much looooooonger line of text and/or sentence"""
d = DocumentWrapper(width=50)
print(d.fill(text))

# First line,
# Another, much looooooonger line of text and/or
# sentence

Это хороший пример изменения метода обертки для сохранения существующих переносов строк и их правильной печати.

Indentation

Наконец, textwrap также включает две функции для отступов, первая из которых – dedent:

# Ugly formatting:
multiline_string = """
First line
Second line
Third line
"""

from textwrap import dedent

multiline_string = """
                   First line
                   Second line
                   Third line
                   """

print(dedent(multiline_string))

# First line
# Second line
# Third line

# Notice the leading blank line...
# You can use:
multiline_string = """\
                   First line
                   Second line
                   Third line
                   """

# or
from inspect import cleandoc
cleandoc(multiline_string)
# 'First line\nSecond line\nThird line'

По умолчанию многострочные строки в Python учитывают все отступы, используемые в строке, поэтому мы должны использовать уродливое форматирование, показанное в первой переменной в приведенном выше фрагменте. Но мы можем использовать функцию dedent для улучшения форматирования – мы просто делаем отступ для значения переменной, как нам нравится, а затем вызываем dedent для нее перед использованием.

В качестве альтернативы можно использовать inspect.cleandoc, которая также удаляет ведущую новую строку. Однако эта функция кодирует пробелы как специальные символы (\n и \t), так что вам, возможно, придется переформатировать ее снова.

Естественно, если есть функция вычитания, то должна быть и функция отступа:

from textwrap import indent

indented = indent(text, "    ", lambda x: not text.splitlines()[0] in x)

Мы просто указываем текст и строку, в которой каждая строка будет отступать (здесь просто 4 пробела, но мы можем, например, использовать >>>, чтобы это выглядело как REPL). Кроме того, мы можем указать предикат, который будет решать, должна ли строка быть отступом или нет. В примере выше лямбда-функция делает так, что первая строка строки (абзац) не имеет отступа.

Заключительные размышления

textwrap – это простой модуль с несколькими функциями/методами, но он еще раз показывает, что Python действительно поставляется с “батарейками в комплекте” для вещей, которые не обязательно должны быть стандартной библиотекой, но они могут сэкономить вам столько времени, когда они вам понадобятся.

Если вы часто работаете с текстом, то я также рекомендую ознакомиться с целым разделом документации, посвященным работе с текстом. Там вы найдете множество модулей и функций, о которых даже не подозревали. 😉

+1
1
+1
0
+1
0
+1
0
+1
0

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *