请教一个Python打印对齐的问题

如图红线所示,为什么打印的结果没有上下对齐,代码逻辑哪里不对吗

img

def query_uid_gid_ugo():
    results = [['/thdfq_khsfb/pnc', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_khsfb/map', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_khsfb/percp', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_khsfb/model', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_khsfb/kznameq_bjzdkmmty', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_plt/hjadernk_service', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_plt/hjadernk', '600>>600>>555', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_plt/thdfqos', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/svp', '200>>200>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_hdmap', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_map/nzpmap', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_map/parkingmap', '600>>600>>777', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_para/bjzdkmmty', '700>>201>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_para/swk', '700>>201>>775', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_knvr/ota_cache', '200>>200>>775', 'uid/gid校验结果异常!预期值为700、201', 'FAILED'],
               ['/thdfq_knvr/logs', '600>>600>>777', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_knvr/mja', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_knvr/eqz', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/dsv_knvr', '200>>200>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_rawknvr', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED']]
    return results


def formatted_print():
    headers = ["Skds/Public", "UID/GID/UGO", "DetailsInfo", "CheckResult"]
    data = query_uid_gid_ugo()

    column_widths = [max(len(str(row[i])) for row in data) for i in range(len(headers))]
    print(column_widths)

    header_line = "\t|\t".join(f"{headers[i]:<{column_widths[i]}}" for i in range(len(headers)))
    print(header_line)

    for row in data:
        row_line = "\t|\t".join(f"{str(item):<{column_widths[i]}}" for i, item in enumerate(row))
        print(row_line)


formatted_print()


出现其他情况格式更加错误

img

问题点: 为什么打印的结果没有上下对齐.
分析思路:
打印的问题从两方面着手:
1.python3, print函数默认输出的编码为utf-8,但是在Python 3中,所有字符串默认都是Unicode编码,
因此编码的不同,会导致统计的长度无效.(Unicode编码下的字符串长度对utf-8的编码有意义么?不含中文的情况下时,没问题)

可以使用以下指令确认打印的输出编码

import sys
print(sys.stdout.encoding)

比较含中文和不含中文的差异

a = 'uid/gid/ugo校验结果正常!'
print(len(a))  # 18
print(len(a.encode('unicode_escape').decode()))  # 53

b = "DetailsInfo"
print(len(b))  # 11
print(len(b.encode('unicode_escape').decode()))  # 11

2.全角和半角的影响

c = 'uid/gid校验结果异常!预期值为700、201'
print(len(c))  # 25
print(len(c.encode('unicode_escape').decode()))  # 85

解决办法:
将全部数据从全角转成半角, 并且转成Unicode编码.这时数据就变得一致了.
缺点:虽然对齐了,中文在Unicode编码下是乱码.

import unicodedata


def query_uid_gid_ugo():
    results = [['/thdfq_khsfb/pnc', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_khsfb/map', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_khsfb/percp', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_khsfb/model', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_khsfb/kznameq_bjzdkmmty', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_plt/hjadernk_service', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_plt/hjadernk', '600>>600>>555', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_plt/thdfqos', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/svp', '200>>200>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_hdmap', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_map/nzpmap', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_map/parkingmap', '600>>600>>777', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_para/bjzdkmmty', '700>>201>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_para/swk', '700>>201>>775', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_knvr/ota_cache', '200>>200>>775', 'uid/gid校验结果异常!预期值为700、201', 'FAILED'],
               ['/thdfq_knvr/logs', '600>>600>>777', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_knvr/mja', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_knvr/eqz', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/dsv_knvr', '200>>200>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_rawknvr', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED']]
    return results

# 全角转半角
def full_to_half(text):
    return unicodedata.normalize('NFKC', text)


def get_max_length(data, _column_widths):
    """
    params: data list
    """
    # _column_widths = [0, 0, 0, 0]
    data_list = []
    for _index, _text in enumerate(data):
        _text_new = full_to_half(_text)
        data_list.append(_text_new)
        # encode('unicode_escape') 将字符串转换为 Unicode 编码,decode() 将编码转换为字符串,
        # 并用 len() 函数计算长度。注意要减去 string.count("\\u"),以排除转义字符对长度的影响。
        text_width = len(_text_new.encode('unicode_escape').decode())  # - _text_new.count("\\u")
        if text_width > _column_widths[_index]:
            _column_widths[_index] = text_width
    return data_list


def formatted_print():
    headers = ["Skds/Public", "UID/GID/UGO", "DetailsInfo", "CheckResult"]
    data = query_uid_gid_ugo()
    data.insert(0, headers)
    column_widths = [0, 0, 0, 0]
    # column_widths = [max(len(str(row[i])) for row in data) for i in range(len(headers))]
    data_new_list = []
    for _index, row in enumerate(data):
        _row = get_max_length(row, column_widths)
        data_new_list.append(_row)

    for _index, row_new in enumerate(data_new_list):
        row_line = "\t|\t".join(f"{item.encode('unicode_escape').decode():{(column_widths[i])}}" for i, item in enumerate(row_new))

        print(row_line)
    print(column_widths)

    # header_line = "\t|\t".join(f"{headers[i]:<{column_widths[i]}}" for i in range(len(headers)))
    # print(header_line)


import sys
print(sys.stdout.encoding)
formatted_print()

这个问题是个头疼的问题,如果全是西文字符,可以完全用python的字符串格式化对齐,
问题出在西文字符与中文字符混合的情况,如果要完全对齐,一般将西文转成全角显示,如果只有固定部分是汉字,比如题中只有第3列有中文,可以只转换第三列为全角,如果字符串中只有后半部是全角,则可以通过补全全角空格来格式化,
下面给出全部转成全角的程序以供参考,虽然字体显示不大好看,但总算对齐了。

def half_all(string):
    l = ''
    for i in string:
        s = ord(i)
        if i == ' ':
            l += ' '
        elif 255 > s >0:
            s = s + 0xfee0
            l += chr(s)
        else:
            l += i
    return l
def query_uid_gid_ugo():
    results = [['/thdfq_khsfb/pnc', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_khsfb/map', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_khsfb/percp', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_khsfb/model', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_khsfb/kznameq_bjzdkmmty', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_plt/hjadernk_service', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_plt/hjadernk', '600>>600>>555', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_plt/thdfqos', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/svp', '200>>200>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_hdmap', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_map/nzpmap', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_map/parkingmap', '600>>600>>777', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_para/bjzdkmmty', '700>>201>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_para/swk', '700>>201>>775', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_knvr/ota_cache', '200>>200>>775', 'uid/gid校验结果异常!预期值为700、201', 'FAILED'],
               ['/thdfq_knvr/logs', '600>>600>>777', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_knvr/mja', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_knvr/eqz', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/dsv_knvr', '200>>200>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_rawknvr', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED']]
    return results


def formatted_print():
    headers = ["Skds/Public", "UID/GID/UGO", "DetailsInfo", "CheckResult"]
    data = query_uid_gid_ugo()

    column_widths = [max(len(str(row[i])) for row in data) for i in range(len(headers))]
    # print(column_widths)

    header_line = "\t|\t".join(half_all(f"{headers[i]:<{column_widths[i]}}") for i in range(len(headers)))
    print(header_line)

    for row in data:
        row_line = "\t|\t".join(half_all(f"{str(item):<{column_widths[i]}}") for i, item in enumerate(row))
        print(row_line)

formatted_print()

img


def query_uid_gid_ugo():
    results = [['/thdfq_khsfb/pnc', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_khsfb/map', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_khsfb/percp', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_khsfb/model', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_khsfb/kznameq_bjzdkmmty', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_plt/hjadernk_service', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_plt/hjadernk', '600>>600>>555', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_plt/thdfqos', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/svp', '200>>200>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_hdmap', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_map/nzpmap', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_map/parkingmap', '600>>600>>777', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_para/bjzdkmmty', '700>>201>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_para/swk', '700>>201>>775', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_knvr/ota_cache', '200>>200>>775', 'uid/gid校验结果异常!预期值为700、201', 'FAILED'],
               ['/thdfq_knvr/logs', '600>>600>>777', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_knvr/mja', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_knvr/eqz', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/dsv_knvr', '200>>200>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_rawknvr', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED']]
    return results

def zh_width(str_val):
    try:
        count = 0
        for word in str_val:  # 检测长宽度中文字符
            if (word >= '\u4e00' and word <= '\u9fa5') or word in [';', ':', ',', '(', ')', '!', '?', '——', '……', '、','》', '《']:
                count += 1
        return count
    except:
        return 0


def formatted_print():
    headers = ["Skds/Public", "UID/GID/UGO", "DetailsInfo", "CheckResult"]
    data = query_uid_gid_ugo()

    column_widths = [max(len(str(row[i])) for row in data) for i in range(len(headers))]
    print(column_widths)

    # 获取最多有多少个中文
    max_zh = [max(zh_width(str(row[i])) for row in data) for i in range(len(headers))]
    print(max_zh)

# 前面减去要补的中文空格数,后面再直接追加相应的中文空格
    header_line = "\t|\t".join(f"{headers[i]:<{column_widths[i]-(max_zh[i]-zh_width(headers[i]))}}{'':{chr(12288)}<{max_zh[i]-zh_width(headers[i])}}" for i in range(len(headers)))
    print(header_line)

    for row in data:
        row_line = "\t|\t".join(f"{str(item):<{column_widths[i]-(max_zh[i]-zh_width(str(item)))}}{'':{chr(12288)}<{max_zh[i]-zh_width(str(item))}}" for i, item in enumerate(row))
        print(row_line)

formatted_print()


中文字符长度问题,可以考虑先encode在计算长度

你没有考虑到中文字符和英文字符的宽度不同。中文字符在终端中通常占据的宽度是一个英文字符的两倍。在计算列宽度时,你需要考虑到字符串中的中文字符。

Python print输出对齐的问题——包含中文无法对齐解决_python输出结果上下无法对齐_欲儿的博客-CSDN博客 原因很简单,因为中文字符默认是全角输出,而英文字符默认为半角, 半角和全角就像英文的 "," 和中文的 "," ,中文的逗号后面会更出一个小空格来,所以造成了这样的不规则。下面的,就是给定字符长度10个字符长度,对齐方式为左边对齐,空余的字符采用 * 填充。将非半角全部转换为半角,话不多说函数如下,调用方法我写在函数的最顶端了。但是当你将字符换为中文的时候,你就会发现它的结果就不那么尽人意了。当然了,我整理了三个方式,左右中,一起打印出来了。那么使用了上面的函数后,运行的代码截图就应该为。_python输出结果上下无法对齐 https://blog.csdn.net/qq_42311391/article/details/126925740

如果结果全是正常会出现这种情况吗?

额,不好意思题主,我刚刚试了一下,代码逻辑是对的,不过没对齐问题没找到。您要是不介意可以试试用第三方库prettytable来打印,这个可以创建一个表格自动调整列宽。

from prettytable import PrettyTable


def query_uid_gid_ugo():
    results = [['/thdfq_khsfb/pnc', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_khsfb/map', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_khsfb/percp', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_khsfb/model', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_khsfb/kznameq_bjzdkmmty', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_plt/hjadernk_service', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_plt/hjadernk', '600>>600>>555', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_plt/thdfqos', '600>>600>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/svp', '200>>200>>550', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_hdmap', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_map/nzpmap', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_map/parkingmap', '600>>600>>777', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_para/bjzdkmmty', '700>>201>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_para/swk', '700>>201>>775', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_knvr/ota_cache', '200>>200>>775', 'uid/gid校验结果异常!预期值为700、201', 'FAILED'],
               ['/thdfq_knvr/logs', '600>>600>>777', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_knvr/mja', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_knvr/eqz', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/dsv_knvr', '200>>200>>770', 'uid/gid/ugo校验结果正常!', 'PASSED'],
               ['/thdfq_rawknvr', '600>>600>>770', 'uid/gid/ugo校验结果正常!', 'PASSED']]
    return results


def formatted_print():
    headers = ["Skds/Public", "UID/GID/UGO", "DetailsInfo", "CheckResult"]
    data = query_uid_gid_ugo()

    table = PrettyTable(headers)
    table.align = "l"

    for row in data:
        table.add_row(row)

    print(table)

formatted_print()

一种简单的方法是使用一个自定义的函数来计算字符串的实际宽度,然后根据这个宽度来对齐。例如,您可以定义如下的函数:
def get_width(s): # 计算字符串的实际宽度,中文字符算两个,英文字符算一个 width = 0 for c in s: if ord© > 127: # 判断是否是中文字符 width += 2 else: width += 1 return width
然后在您的formatted_print函数中,使用这个函数来替换原来的column_widths列表:
column_widths = [max(get_width(str(row[i])) for row in data) for i in range(len(headers))]

该回答通过自己思路及引用到GPTᴼᴾᴱᴺᴬᴵ搜索,得到内容具体如下:
根据您提供的代码和截图,您想要实现一个打印对齐的功能,但是目前的结果没有达到预期的上下对齐效果。问题出在您计算每列的最大宽度时,没有考虑到每个中文字符的宽度为2个英文字符。
为了解决这个问题,您可以按照以下步骤进行修改:
1、 引入unicodedata模块,用于计算字符串长度时考虑中文字符的宽度。

import unicodedata

2、 修改计算列宽的部分,将每个字符的宽度乘以相应的权重,考虑中文字符的实际宽度。

column_widths = [max(max(unicodedata.east_asian_width(str(row[i])) == 'W', len(str(row[i]))) for row in data) * 2 for i in range(len(headers))]

3、 修改打印部分,使用\t制表符进行对齐,并在每列之间添加竖线分隔符。

header_line = " | ".join(f"{headers[i]:^{column_widths[i]}}" for i in range(len(headers)))
print(header_line)

for row in data:
    row_line = " | ".join(f"{str(item):^{column_widths[i]}}" for i, item in enumerate(row))
    print(row_line)

这样修改后,您应该能够得到更加对齐的打印结果。请注意,中文字符的宽度在计算时被乘以2,以确保对齐效果正确。

希望这个解决方案能够帮助到您!


如果以上回答对您有所帮助,点击一下采纳该答案~谢谢

我建议用prettytable来

在Python中实现print输出按列对齐的多种方法播
使用%格式按列对齐打印输出
可以参考下

引用gpt作答:
其中一个问题是在计算列宽时未考虑到列标题的长度。由于行的数据中很长的字符串,导致列宽度的计算不准确,从而导致对齐问题。

您可以尝试更改计算列宽的部分,将列标题的长度也考虑在内。这里是修改后的 formatted_print 函数:

def formatted_print():
    headers = ["Skds/Public", "UID/GID/UGO", "DetailsInfo", "CheckResult"]
    data = query_uid_gid_ugo()

    # 计算列宽时考虑到列标题的长度
    column_widths = [max(len(item), len(headers[i])) for i, item in enumerate(headers)]
    print(column_widths)

    # 打印表头
    header_line = "\t|\t".join(f"{headers[i]:<{column_widths[i]}}" for i in range(len(headers)))
    print(header_line)

    # 打印数据行
    for row in data:
        row_line = "\t|\t".join(f"{str(item):<{column_widths[i]}}" for i, item in enumerate(row))
        print(row_line)


formatted_print()

【相关推荐】




如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^