You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决ANSI颜色代码导致Python .format()表格排版错乱问题

终端表格添加颜色后排版错乱的修复方案

问题描述

为表格中的数值添加ANSI颜色后,原本通过str.format()设置的排版间距失效,格式变为基于带颜色代码的字符串长度排版,导致表格对齐混乱。未添加颜色时输出正常,添加颜色后数值字符串变为包含ANSI转义序列的格式(如'\x1b[38;2;0;255;0m20\x1b[38;2;255;255;255m')。

相关字典定义:

items = {
    'staff'     : ['Staff',          20, 0,'Yes', 'None', 'None', 'None', ' ',  5,12],
    'club'      : ['Club',           75, 0,'Yes', 'None', 'None', 'None', ' ', 15,22],
    'mace'      : ['Mace',           200,0,'Yes', 'None', 'None', 'None', ' ', 30,371],
    'warhammer' : ['Warhammer',      350,0,'Yes', 'None', 'None', 'None', ' ', 50,60]
}
argv = ('staff', 'club', 'mace', 'warhammer')

相关代码:

def color(r, g, b, text):
    return str("\033[38;2;{};{};{}m{} \033[38;2;255;255;255m".format(r, g, b, text))
x = 0
i = 1
print("{:<0} {:<12} {:<10} {:<10} {:<6}".format(" ", "Item", "Cost", "Damage", "Owned"))
while x < i - 1:
    y = listing(x, argv, False, 0,1,8,9,3) #从字典输出列表
    if y[1] > player['coins']:
        y[1] = color(255,0,0,(y[1]))
    else:
        y[1] = color(0,255,0,(y[1]))
    z = options.get(x+1) #获取选择箭头,此处无关
    w = {z:y}
    for k, v in w.items():
        item, cost, damagemin, damagemax, owned = v
        print("{:<0} {:<12} {:<6} {:>03d}-{:>03d}    {:<5}".format(k, item, cost, damagemin, damagemax, owned))
    x += 1

原因分析

  1. ANSI转义序列被计入长度:Python的str.format()会将ANSI颜色控制字符(如\x1b[38;2;0;255;0m)计入字符串总长度,但终端显示时这些控制字符不会占据视觉宽度,导致对齐计算错误。
  2. 多余空格:color函数中在text后额外添加了一个空格,进一步增加了字符串长度,加重排版错乱。

解决方案

方案1:手动校正宽度(无需额外库)

步骤1:修复color函数

移除text后的多余空格,避免额外长度干扰:

def color(r, g, b, text):
    # 移除text后的多余空格,使用f-string简化写法
    return f"\033[38;2;{r};{g};{b}m{text}\033[38;2;255;255;255m"

步骤2:基于原始数值长度格式化

保存原始数值的长度,在格式化时以此为基准设置宽度,忽略颜色代码的长度:

x = 0
i = len(argv)  # 修正循环条件,确保遍历所有物品
print("{:<0} {:<12} {:<10} {:<10} {:<6}".format(" ", "Item", "Cost", "Damage", "Owned"))
while x < i:
    y = listing(x, argv, False, 0,1,8,9,3)
    # 保存原始cost的字符串长度
    original_cost_len = len(str(y[1]))
    if y[1] > player['coins']:
        y[1] = color(255,0,0, y[1])
    else:
        y[1] = color(0,255,0, y[1])
    z = options.get(x+1)
    w = {z:y}
    for k, v in w.items():
        item, cost, damagemin, damagemax, owned = v
        # 使用原始长度作为格式化宽度
        print("{:<0} {:<12} {:<{width}} {:>03d}-{:>03d}    {:<5}".format(
            k, item, cost, damagemin, damagemax, owned, width=original_cost_len
        ))
    x += 1

方案2:使用wcwidth库自动计算显示宽度

wcwidth库能准确计算字符串在终端中的实际显示宽度,自动忽略ANSI控制字符。

步骤1:安装库

pip install wcwidth

步骤2:修改代码适配

from wcwidth import wcswidth

def color(r, g, b, text):
    return f"\033[38;2;{r};{g};{b}m{text}\033[38;2;255;255;255m"

x = 0
i = len(argv)
print("{:<0} {:<12} {:<10} {:<10} {:<6}".format(" ", "Item", "Cost", "Damage", "Owned"))
while x < i:
    y = listing(x, argv, False, 0,1,8,9,3)
    if y[1] > player['coins']:
        y[1] = color(255,0,0, y[1])
    else:
        y[1] = color(0,255,0, y[1])
    z = options.get(x+1)
    w = {z:y}
    for k, v in w.items():
        item, cost, damagemin, damagemax, owned = v
        # 计算cost的实际显示宽度
        display_width = wcswidth(cost)
        # 目标宽度设为6,计算需要补充的空格数
        padding = max(0, 6 - display_width)
        aligned_cost = cost + " " * padding
        # 替换原格式化中的cost为对齐后的版本
        print("{:<0} {:<12} {} {:>03d}-{:>03d}    {:<5}".format(
            k, item, aligned_cost, damagemin, damagemax, owned
        ))
    x += 1

内容的提问来源于stack exchange,提问作者iWECHAMPIONSi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 15:47:53