整形された出力は、ログや表の見やすさを大きく左右します。
本記事では、Pythonで文字列や数値の「幅」を揃える基本と実践を、初心者の方にもわかりやすく段階的に解説します。
ljust
、rjust
、center
、zfill
と、f-string
やformat
による書式指定、さらに全角文字(日本語)対応や切り詰めのコツまで丁寧に説明します。
P文字列メソッドで幅を揃える
Pythonの文字列メソッドは、最小限のコードで左寄せ・右寄せ・中央寄せ・0埋めによる幅揃えを実現できます。
ここでは挙動の違いと、見えづらい空白を可視化するための角かっこを使って確認します。
左寄せ: ljust()
ljust(width, fillchar=' ')
は、指定幅に満たない場合に右側へ埋め文字(既定は空白)を追加して左寄せします。
# 左寄せの基本
s = "cat"
print("[" + s.ljust(8) + "]") # 空白で右側を埋める
print("[" + s.ljust(8, ".") + "]") # '.' で右側を埋める
print("[" + "長い文字列".ljust(4) + "]") # 幅より長いときは元の文字列のまま(切り詰めない)
[cat ]
[cat.....]
[長い文字列]
右寄せ: rjust()
rjust(width, fillchar=' ')
は左側へ埋め文字を追加して右寄せします。
s = "cat"
print("[" + s.rjust(8) + "]")
print("[" + s.rjust(8, "_") + "]")
[ cat]
[_____cat]
中央寄せ: center()
center(width, fillchar=' ')
は左右に均等に埋め文字を追加します。
余りが出る場合は右側に1文字多く入ります。
s = "cat"
print("[" + s.center(8) + "]")
print("[" + s.center(8, "-") + "]")
[ cat ]
[--cat---]
0埋め: zfill()
zfill(width)
は左側を0
で埋めます。
数値文字列の先頭にある符号(+
/-
)がある場合は、その直後に0を挿入する点が特長です。
print("42".zfill(5)) # 5桁に0埋め
print("-42".zfill(5)) # 符号の直後に0が入る
print("+42".zfill(5)) # 同上
print("0x1a".zfill(6)) # '0x' は符号ではないので先頭から埋まる
00042
-0042
+0042
000x1a
埋め文字を指定する
ljust
、rjust
、center
では第2引数fillchar
に1文字だけ指定できます。
複数文字はエラーになるため注意してください。
zfill
は埋め文字を変更できません。
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)
でも同じ指定が使えます。
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
は特別な「フラグ」で、符号の直後からゼロ埋めする「数値寄せ(=)」が有効になります。
# 文字列を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文字 + 寄せ記号」の順で指定します。
v = "py"
print(f"[{v:*^8}]") # '*'で中央寄せ
print(f"[{v:.<8}]") # '.'で左寄せ
print(f"[{v:_>8}]") # '_'で右寄せ
[***py***]
[py......]
[______py]
数値の桁と符号を揃える :06d :+08.2f
符号を揃えるには+
(常に符号を表示)を使い、桁数や小数点以下の桁は幅.精度
で指定します。
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
フラグを併用するとプレフィックスの後からゼロ埋めされます。
n = 0x1A
print(f"[{n:#06x}]") # 幅6、'0x'付き、ゼロ埋め
[0x001a]
実用例と注意点
現場で使う際の定番パターンと、知っておくとハマりにくい注意点を整理します。
表やログの整形
列ごとに寄せ方を変えると読みやすい表になります。
見出しと区切り線を入れるとさらに明瞭です。
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
数値は右寄せ、文字列は左寄せにすると桁が見やすくなります。
日本語や全角文字の幅
ljust
やrjust
、center
、および書式指定の「幅」は、基本的に「文字数」に対して働きます。
しかし、端末や等幅フォントでは全角文字(日本語)が「見た目で2桁分」に表示されることが多く、列がズレて見える場合があります。
names = ["Taro", "太郎", "Hanako", "山田太郎"]
for n in names:
# 幅8で左寄せし、'|'で区切りを可視化
print(f"|{n:<8}|end")
|Taro |end
|太郎 |end
|Hanako |end
|山田太郎 |end
等幅フォントの端末では「太郎」「山田太郎」が実質2倍幅のため、見た目の境界が合わないことがあります。
これを解消するには「表示幅」に基づいてパディングする関数を使います。
厳密には複合絵文字や結合文字などもあり難しいため、実務ではwcwidth
ライブラリの利用をおすすめします。
以下は標準ライブラリunicodedata
で全角(幅2)と半角(幅1)を概算し、表示幅で左右寄せする簡易版です。
# 概算: 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
は外部ライブラリなので導入が必要です。pipの場合は以下のコマンドでインストールできます。
pip install wcwidth
# 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前提ならスライスで十分ですが、日本語対応では「表示幅」を見ながら切るのが安全です。
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;
などを指定して、スペースや改行を保ちつつ等幅で表示します。 - 端末やフォントにより、全角や一部絵文字の見かけ幅が異なることがあります。日本語を含む表を扱う場合は、実機の表示で最終確認するのが確実です。
確認用に、縦境界を出して目視チェックするとズレを発見しやすいです。
items = ["A", "あ", "AB", "山田", "🙂", "👍🏽"] # 絵文字は環境により幅が異なることあり
for it in items:
print(f"|{it:<6}| <- {repr(it)}")
|A | <- 'A'
|あ | <- 'あ'
|AB | <- 'AB'
|山田 | <- '山田'
|🙂 | <- '🙂'
|👍🏽 | <- '👍🏽'
環境によっては絵文字の列が揃わないことがあります。
その場合はwcwidth
や実表示での検証を併用してください。
まとめ
本記事では、Pythonで文字列や数値の幅を揃える基本から実用までを解説しました。
文字列メソッドのljust
、rjust
、center
は手軽に寄せを実現でき、zfill
は符号に配慮したゼロ埋めが可能です。
f-stringやformat
では:<
、:>
、:^
で寄せ、0
フラグや+
、#
、精度指定を組み合わせることで、ログや表に適したきめ細やかな整形が行えます。
実務上は、全角文字や絵文字による「見た目の幅」のズレが最大の落とし穴です。
標準の幅指定は「文字数」に基づくため、日本語を含む表やレポートでは「表示幅ベースのパディング/切り詰め」を導入するのが有効です。
簡易にはunicodedata
、より実態に近づけるにはwcwidth
を用い、最終的には等幅フォント環境での目視確認を欠かさないようにしましょう。
これらの基本と注意点を押さえておけば、ログやCLIツール、テキストレポートの整形品質が安定し、読みやすさが大きく向上します。