閉じる

Pythonの文字列幅を揃える方法: 0埋め・左寄せ・右寄せ・中央寄せ

整形された出力は、ログや表の見やすさを大きく左右します。

本記事では、Pythonで文字列や数値の「幅」を揃える基本と実践を、初心者の方にもわかりやすく段階的に解説します。

ljustrjustcenterzfillと、f-stringformatによる書式指定、さらに全角文字(日本語)対応や切り詰めのコツまで丁寧に説明します。

P文字列メソッドで幅を揃える

Pythonの文字列メソッドは、最小限のコードで左寄せ・右寄せ・中央寄せ・0埋めによる幅揃えを実現できます。

ここでは挙動の違いと、見えづらい空白を可視化するための角かっこを使って確認します。

左寄せ: ljust()

ljust(width, fillchar=' ')は、指定幅に満たない場合に右側へ埋め文字(既定は空白)を追加して左寄せします。

Python
# 左寄せの基本
s = "cat"
print("[" + s.ljust(8) + "]")          # 空白で右側を埋める
print("[" + s.ljust(8, ".") + "]")     # '.' で右側を埋める
print("[" + "長い文字列".ljust(4) + "]")  # 幅より長いときは元の文字列のまま(切り詰めない)
実行結果
[cat     ]
[cat.....]
[長い文字列]

右寄せ: rjust()

rjust(width, fillchar=' ')は左側へ埋め文字を追加して右寄せします。

Python
s = "cat"
print("[" + s.rjust(8) + "]")
print("[" + s.rjust(8, "_") + "]")
実行結果
[     cat]
[_____cat]

中央寄せ: center()

center(width, fillchar=' ')は左右に均等に埋め文字を追加します。

余りが出る場合は右側に1文字多く入ります。

Python
s = "cat"
print("[" + s.center(8) + "]")
print("[" + s.center(8, "-") + "]")
実行結果
[  cat   ]
[--cat---]

0埋め: zfill()

zfill(width)は左側を0で埋めます。

数値文字列の先頭にある符号(+/-)がある場合は、その直後に0を挿入する点が特長です。

Python
print("42".zfill(5))     # 5桁に0埋め
print("-42".zfill(5))    # 符号の直後に0が入る
print("+42".zfill(5))    # 同上
print("0x1a".zfill(6))   # '0x' は符号ではないので先頭から埋まる
実行結果
00042
-0042
+0042
000x1a

埋め文字を指定する

ljustrjustcenterでは第2引数fillcharに1文字だけ指定できます。

複数文字はエラーになるため注意してください。

zfillは埋め文字を変更できません。

Python
s = "py"
print("[" + s.center(7, "*") + "]")   # '*'で中央寄せ
print("[" + s.ljust(7, "•") + "]")    # 中点で左寄せ
# print(s.rjust(7, "ab"))  # これはエラー(TypeError)になる
実行結果
[**py***]
[py•••••]

補足として、これらの幅指定は「最小幅」です。

対象が指定幅より長い場合、切り詰めは行われず、そのまま返されます。

切り詰めは後述の「長い文字列の切り詰め」で扱います。

f-stringとformatで幅と寄せを指定

f-stringやstr.formatは、同じミニ言語(書式指定子)で柔軟に寄せ・幅・ゼロ埋め・符号・精度などを指定できます。

数値の整形では0フラグや+フラグ、#(基数プレフィックス)などが便利です。

基本の書式 :< :> :^ と幅

:<(左寄せ)、:>(右寄せ)、:^(中央寄せ)と幅を組み合わせます。

以下はf-stringですが、"{}".format(x)でも同じ指定が使えます。

Python
text = "cat"
print(f"[{text:<8}]")   # 左寄せ 幅8
print(f"[{text:>8}]")   # 右寄せ 幅8
print(f"[{text:^8}]")   # 中央寄せ 幅8

# .format でも同じ
print("[{:*^8}]".format(text))  # '*'で中央寄せ
実行結果
[cat     ]
[     cat]
[  cat   ]
[**cat***]

注意: 幅は「最低限の幅」です。

文字列が長い場合はそのまま出力され、幅を超えます。

0埋め :0> と桁数

文字列を0で埋めたい場合はfill='0'として0>などと書けます。

一方、数値では0は特別な「フラグ」で、符号の直後からゼロ埋めする「数値寄せ(=)」が有効になります。

Python
# 文字列を0で右寄せ(単なる埋め文字としての '0')
s = "cat"
print(f"[{s:0>8}]")      # 先頭に '0' が入る

# 数値のゼロ埋め(= 寄せ、符号の直後に0を入れる)
n1, n2 = 42, -42
print(f"[{n1:05d}]")     # '0'フラグ + 幅5 -> 00042
print(f"[{n2:05d}]")     # 符号の直後に0 -> -0042

# 右寄せ + '0'を埋め文字として指定すると挙動が異なる点
print(f"[{n2:0>5}]")     # 符号も文字として右寄せ -> 00-42 (望む結果ではないことが多い)
実行結果
[00000cat]
[00042]
[-0042]
[00-42]

数値に対してゼロ埋めしたい場合は、:{0}{width}{type}0フラグ(:05d:08.2f)を使うのが安全です。

埋め文字の変更 :*^ など

埋め文字は「1文字 + 寄せ記号」の順で指定します。

Python
v = "py"
print(f"[{v:*^8}]")   # '*'で中央寄せ
print(f"[{v:.<8}]")   # '.'で左寄せ
print(f"[{v:_>8}]")   # '_'で右寄せ
実行結果
[***py***]
[py......]
[______py]

数値の桁と符号を揃える :06d :+08.2f

符号を揃えるには+(常に符号を表示)を使い、桁数や小数点以下の桁は幅.精度で指定します。

Python
nums = [-3.5, 12.3, 0]
for x in nums:
    # 幅8、常に符号、有効桁: 小数点以下2桁、ゼロ埋め
    print(f"[{x:+08.2f}]")

# 整数のゼロ埋め
a, b = 42, -42
print(f"[{a:06d}]")   # 6桁ゼロ埋め
print(f"[{b:06d}]")   # 符号直後に0
実行結果
[-003.50]
[+0012.30]
[+0000.00]
[000042]
[-00042]

基数プレフィックスを含む例(16進数)では、#0フラグを併用するとプレフィックスの後からゼロ埋めされます。

Python
n = 0x1A
print(f"[{n:#06x}]")   # 幅6、'0x'付き、ゼロ埋め
実行結果
[0x001a]

実用例と注意点

現場で使う際の定番パターンと、知っておくとハマりにくい注意点を整理します。

表やログの整形

列ごとに寄せ方を変えると読みやすい表になります。

見出しと区切り線を入れるとさらに明瞭です。

Python
rows = [
    (1,  "Alice", 82.5),
    (2,  "Bob",   9.0),
    (10, "Chloe", 103.4),
]

# 見出し
print(f"{'ID':>4} | {'Name':<10} | {'Score':>7}")
print("-" * 4 + "-+-" + "-" * 10 + "-+-" + "-" * 7)

# 行
for rid, name, score in rows:
    print(f"{rid:>4} | {name:<10} | {score:>7.1f}")
実行結果
  ID | Name       |   Score
----+------------+-------
   1 | Alice      |    82.5
   2 | Bob        |     9.0
  10 | Chloe      |   103.4

数値は右寄せ、文字列は左寄せにすると桁が見やすくなります。

日本語や全角文字の幅

ljustrjustcenter、および書式指定の「幅」は、基本的に「文字数」に対して働きます。

しかし、端末や等幅フォントでは全角文字(日本語)が「見た目で2桁分」に表示されることが多く、列がズレて見える場合があります。

Python
names = ["Taro", "太郎", "Hanako", "山田太郎"]
for n in names:
    # 幅8で左寄せし、'|'で区切りを可視化
    print(f"|{n:<8}|end")
実行結果
|Taro    |end
|太郎      |end
|Hanako  |end
|山田太郎  |end

等幅フォントの端末では「太郎」「山田太郎」が実質2倍幅のため、見た目の境界が合わないことがあります。

これを解消するには「表示幅」に基づいてパディングする関数を使います。

厳密には複合絵文字や結合文字などもあり難しいため、実務ではwcwidthライブラリの利用をおすすめします。

以下は標準ライブラリunicodedataで全角(幅2)と半角(幅1)を概算し、表示幅で左右寄せする簡易版です。

Python
# 概算: East Asian Width に基づき W(全角)とF(全角)を2、それ以外を1とみなす
import unicodedata

def disp_width(s: str) -> int:
    return sum(2 if unicodedata.east_asian_width(ch) in "WF" else 1 for ch in s)

def ljust_display(s: str, width: int, fill: str = " ") -> str:
    w = disp_width(s)
    if w >= width:
        return s
    return s + fill * (width - w)

def rjust_display(s: str, width: int, fill: str = " ") -> str:
    w = disp_width(s)
    if w >= width:
        return s
    return fill * (width - w) + s

def center_display(s: str, width: int, fill: str = " ") -> str:
    w = disp_width(s)
    if w >= width:
        return s
    left = (width - w) // 2
    right = width - w - left
    return fill * left + s + fill * right

# デモ
names = ["Taro", "太郎", "Hanako", "山田太郎"]
for n in names:
    print("|" + ljust_display(n, 8) + "|end")
実行結果
|Taro    |end
|太郎    |end
|Hanako  |end
|山田太郎|end

より正確に表示幅を扱うには、端末の実装に近いwcwidth(サードパーティ)を使います。

wcwidthはインストールが必要

wcwidthは外部ライブラリなので導入が必要です。pipの場合は以下のコマンドでインストールできます。

Shell
pip install wcwidth
Python
# pip install wcwidth で導入後
from wcwidth import wcswidth

def ljust_wcwidth(s: str, width: int, fill: str = " ") -> str:
    w = wcswidth(s)
    if w < 0:  # 非表示制御文字などの場合 -1
        w = len(s)
    if w >= width:
        return s
    return s + fill * (width - w)

for n in ["Taro", "太郎", "Hanako", "山田太郎"]:
    print("|" + ljust_wcwidth(n, 8) + "|end")
実行結果
|Taro    |end
|太郎    |end
|Hanako  |end
|山田太郎|end

注意: 絵文字の肌色バリエーションや家族絵文字のように複数のコードポイントで1つの見た目を構成するケースは難度が高く、wcwidthでも環境差が出ることがあります。

長い文字列の切り詰め

幅を超えるとき、Pythonの幅指定は「切り詰め」をしません。

整形表では列をそろえるため、はみ出した分を省略記号などで切り詰めることがよくあります。

ASCII前提ならスライスで十分ですが、日本語対応では「表示幅」を見ながら切るのが安全です。

Python
import unicodedata

def disp_width(s: str) -> int:
    return sum(2 if unicodedata.east_asian_width(ch) in "WF" else 1 for ch in s)

def truncate_display(s: str, width: int, placeholder: str = "…") -> str:
    """表示幅でwidthに収め、超える場合はplaceholderを末尾に付与する"""
    p_w = disp_width(placeholder)
    if width <= p_w:
        return placeholder  # 幅が極端に小さい場合の安全策
    limit = width - p_w
    total = 0
    out = []
    for ch in s:
        w = 2 if unicodedata.east_asian_width(ch) in "WF" else 1
        if total + w > limit:
            break
        out.append(ch)
        total += w
    return "".join(out) + placeholder

samples = [
    "This is a very long title",
    "とても長い見出しタイトルです",
    "混在MixedあいうえおABC",
]

for t in samples:
    cut = truncate_display(t, 12)
    print(f"|{cut:<12}|")  # 12幅で左寄せ表示
実行結果
|This is a v…|
|とても長い…      |
|混在Mixedあ…   |

textwrap.shortenは英単語中心の文を省略する用途に便利ですが、見た目の等幅整列とは目的が異なるため、列レイアウトでは上記のような幅ベースのカスタム関数の方が合致します。

フォントの等幅表示を確認

整列表示は「等幅フォント」でなければ視覚的に成立しません。

プロポーショナルフォント(文字ごとに幅が違うフォント)では、空白の数が同じでも列が揃いません。

以下のポイントを押さえてください。

  • ターミナルやエディタでは等幅フォントを選択します。
  • Webに出す場合はCSSでfont-family: monospace; white-space: pre;などを指定して、スペースや改行を保ちつつ等幅で表示します。
  • 端末やフォントにより、全角や一部絵文字の見かけ幅が異なることがあります。日本語を含む表を扱う場合は、実機の表示で最終確認するのが確実です。

確認用に、縦境界を出して目視チェックするとズレを発見しやすいです。

Python
items = ["A", "あ", "AB", "山田", "🙂", "👍🏽"]  # 絵文字は環境により幅が異なることあり
for it in items:
    print(f"|{it:<6}| <- {repr(it)}")
実行結果
|A     | <- 'A'
|あ    | <- 'あ'
|AB    | <- 'AB'
|山田  | <- '山田'
|🙂     | <- '🙂'
|👍🏽    | <- '👍🏽'

環境によっては絵文字の列が揃わないことがあります。

その場合はwcwidthや実表示での検証を併用してください。

まとめ

本記事では、Pythonで文字列や数値の幅を揃える基本から実用までを解説しました。

文字列メソッドのljustrjustcenterは手軽に寄せを実現でき、zfillは符号に配慮したゼロ埋めが可能です。

f-stringやformatでは:<:>:^で寄せ、0フラグや+#、精度指定を組み合わせることで、ログや表に適したきめ細やかな整形が行えます。

実務上は、全角文字や絵文字による「見た目の幅」のズレが最大の落とし穴です。

標準の幅指定は「文字数」に基づくため、日本語を含む表やレポートでは「表示幅ベースのパディング/切り詰め」を導入するのが有効です。

簡易にはunicodedata、より実態に近づけるにはwcwidthを用い、最終的には等幅フォント環境での目視確認を欠かさないようにしましょう。

これらの基本と注意点を押さえておけば、ログやCLIツール、テキストレポートの整形品質が安定し、読みやすさが大きく向上します。

この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!