Pythonで複数行の文字列の行頭の空白を削除する

textwrapという標準ライブラリを最近知り、その中にdedentという便利なメソッドがあったのでその紹介です。
参考: textwrap — テキストの折り返しと詰め込み — Python 3.11.0b5 ドキュメント

ドキュメントのページタイトルにある通り、本来は長いテキストを折り返すためのライブラリです。

さて、Pythonでは基本的な技術ですが、三重引用符(“””か、”’)で囲むことによって、複数行のテストオブジェクトを生成できます。
参考: テキストシーケンス型

これをやるときに、コードの見た目をきれいにするためにインデントをつけると、こんな感じになってしまいます。(あくまでも例として出してるサンプルコードであって、走れメロスの本文を属性に持つクラスを作りたかったわけではありません。)

class foo():
    def __init__(self):
        self.text = """
            メロスは激怒した。
            必ず、かの邪智暴虐の王を除かなければならぬと決意した。
            メロスには政治がわからぬ。
            メロスは、村の牧人である。
            笛を吹き、羊と遊んで暮して来た。
            けれども邪悪に対しては、人一倍に敏感であった。
        """


obj = foo()
print(obj.text)
# 以下出力

            メロスは激怒した。
            必ず、かの邪智暴虐の王を除かなければならぬと決意した。
            メロスには政治がわからぬ。
            メロスは、村の牧人である。
            笛を吹き、羊と遊んで暮して来た。
            けれども邪悪に対しては、人一倍に敏感であった。
        

これをやると、各行の先頭に要らない半角スペースが入ってしまいます。上記のコードの例であれば各行12個入ってます。ついでに前後に不要な改行があり、空白行がそれぞれできています。これを避けるには次のように書かなければいけません。

class bar():
    def __init__(self):
        self.text = """メロスは激怒した。
必ず、かの邪智暴虐の王を除かなければならぬと決意した。
メロスには政治がわからぬ。
メロスは、村の牧人である。
笛を吹き、羊と遊んで暮して来た。
けれども邪悪に対しては、人一倍に敏感であった。"""


obj = bar()
print(obj.text)
# 以下出力
メロスは激怒した。
必ず、かの邪智暴虐の王を除かなければならぬと決意した。
メロスには政治がわからぬ。
メロスは、村の牧人である。
笛を吹き、羊と遊んで暮して来た。
けれども邪悪に対しては、人一倍に敏感であった。

上のコードくらい短ければいいのですが、長いコードにこういうのが入ると非常に不恰好です。実際日本語の文章がこんなダラダラコード中にハードコーディングされることは滅多にないのですがそれはさておき。

ここで、先述のtextwrap.dedentを使うと、そのメソッドが行頭の空白を消してくれます。

良い点でもあるのですが、テキスト中の「各行に共通する空白」だけ消します。空白が4個の行と8個の行が混在していたら、各行から4個消えて、元々8個存在してた行には4個スペースが残るので、相対的なインデントは保持されるということです。これは結構良い仕様です。

ちなみに、前後の改行コードは消してくれないので、それはそれで、strip()から何かで消します。

これを使うと次のようになります。

import textwrap


# 最初のコード例のクラスのインスタンスで実験
print(textwrap.dedent(obj.text).strip())
# 以下出力
メロスは激怒した。
必ず、かの邪智暴虐の王を除かなければならぬと決意した。
メロスには政治がわからぬ。
メロスは、村の牧人である。
笛を吹き、羊と遊んで暮して来た。
けれども邪悪に対しては、人一倍に敏感であった。

まずdedentを適用して、その結果に対してstrip()をするのが大事です。逆にすると意図せぬ結果になります。

これで、不要な行頭の空白が消えました。

おまけですが、逆に行頭に空白に限らず何かしらの文字列を挿入する、textwrap.indent もあります。これは、テキストと、挿入したい文字列を入れたらいいですね。例えば、 果物の名前の先頭に – (ハイフン) でも差し込みましょうか。

sample_text = """
    りんご
    みかん
    もも
    なし
"""
sample_text = textwrap.dedent(sample_text).strip()  # まず不要な空白消す

print(textwrap.indent(sample_text, "- "))  # 先頭に - 挿入
# 以下出力
"""
- りんご
- みかん
- もも
- なし
"""

このほかにも、textwrapには文字列を折り返したり切り詰めたりするなどの便利なメソッドが用意されています(というより本来そのためのライブラリです)ので、そのうち紹介しようと思います。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です