如何解决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
原因分析
- ANSI转义序列被计入长度:Python的
str.format()会将ANSI颜色控制字符(如\x1b[38;2;0;255;0m)计入字符串总长度,但终端显示时这些控制字符不会占据视觉宽度,导致对齐计算错误。 - 多余空格:
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
相关产品推荐
相关产品推荐

