python print中文不对齐问题(特殊字符、混合字符不对齐问题)的解决

目录

效果展示及解决方案
问题分析
解决方案
个人感悟

效果展示及解决方案

        下方左图为问题展示,右图为解决效果

        适用范围:中文英文特殊字符的混合字符串

        解决代码

def is_chinese(uchar):
    # 判断当前字符是否为中文字符
    return uchar >= u'\u4e00' and uchar <= u'\u9fa5'


def func(ustring):
    # 输入一个字符串,输出一个字符串
    # 输出字符串的显示宽度为指定宽度(非域宽)
    width = 20 * 3
    # 将20改为你想要的显示宽度(默认全角)
    # 注意显示宽度应当大于字符串中字符个数
    for uchar in ustring:
        width -= 3 if is_chinese(uchar) else 2
    return ustring + ' ' * int(width / 2) if width % 2 == 0 else ustring + '\u3000' + ' ' * int((width - 3) / 2)


# inf 为储存信息的字典,其他数据结构亦可(仅做展示),确保name处为字符串
for name, score in inf.items():
    name_new = func(name)
    print(" name:{} \t scores:{:<8}".format(name_new, score))
不想深究的话到这里就可以了,后面就是解决的过程。其实也没写多少东西,主要是其他文章在某些点上写的比我好,但是看完了的话就相当于自己踩了一遍坑,我就写了自己的真实感悟,提供的方案的适用领域更广,想学具体的知识点的话请划走。

问题分析

1.背景

        因为某种原因(又是社团的活),要绘制一个榜单,然后就遇到了这个中文不对齐问题,我研究(瞎琢磨)了几个小时,发现这个东西简直就是一个巨坑,我断言(丢脸),这个问题永远只能优化而得不到解决,除非另辟蹊径或者文明重启!

        嘴上说的狠,但是ddl在催(哭)。我搜索到了好多解决方案,但是都不满意,下文我会一一列举,最后xzh学长(不亏是学长)提供了一个思路,然后我自己写了一个解决方案,最后算是解决了,于是决定写这篇博客记录一下。

2.解决过程

        我的活是制作一个榜单,榜单上是协会成员自己起的用户名,每个人都有自己的累计成绩,最后进行排名后打印出来榜单。我将所有信息读取到文档之后,利用字典统计了所有人的信息,然后排序得到一个列表,然后打印信息,然后问题就出现了,中文不对齐!

        下方inf为二维列表变量,每个成员储存用户名和用户分数两个数据,代码中就不注释了。

for i in range(len(new_inf)):
    name = func(new_inf[i][0])
    score = int(new_inf[i][1])
    print("#{:<5} name:{:<20} ".format(i + 1, new_inf[i][0]), end="")
    print("socres:{:<8} complete:{:.0%}".format(score, (score/(num_eg * 100))))

        然后我没有意识到问题的严重行,直接用了制表符\t,然后情况有所好转但未解决

for i in range(len(new_inf)):
    name = func(new_inf[i][0])
    score = int(new_inf[i][1])
    print("#{:<5} name:{:<20} ".format(i + 1, new_inf[i][0]), end="")
    print("\tsocres:{:<8} complete:{:.0%}".format(score, (score/(num_eg * 100))))

        然后脑子抽了,想到了更新符\r,然后谜之操作后得到了下图

for i in range(len(new_inf)):
    name = func(new_inf[i][0])
    score = int(new_inf[i][1])
    print("#{:<5} name:{:<20} ".format(i + 1, new_inf[i][0]), end="")
    print("\r\t\t\t\t\t\t\t\tsocres:{:<8} complete:{:.0%}".format(score, (score/(num_eg * 100))))

        其实效果也达到了只是牺牲了一些东西(bushi)

        然后就看看网上的大神们的方案呗,这个时候才发现问题的复杂性,大神们解决的问题没有我这个复杂,无可奈何的情况下看看有没有库吧,然后真发现了一个tabulate库,然后看了看效果图,算了好丑,而且还是封装的,最后决定自己写,写的时候想着将所有字符的视宽调成一样不就行了吗,然后得到了下图,目的达成成了,但是还是不美观。

def half_all(uchar):
    # 单个字符 半角转全角
    swap = ord(uchar)
    if swap < 0x0020 or swap > 0x7e:    # 全角字符直接返回
        return uchar
    # 除了空格其他的全角半角的公式为: 半角 = 全角 - 0xfee0
    # 特判
    if swap == 0x0020:
        swap = 0x3000
    else:
        swap += 0xfee0
    return chr(swap)


def func1(ustring):
    # 把字符串全角转半角
    return "".join([half_all(uchar) for uchar in ustring])


for i in range(len(new_inf)):
    name = func1(new_inf[i][0])
    score = int(new_inf[i][1])
    print("#{:<5} name:{:\u3000<20} ".format(i + 1, name), end=" ")
    print("\tsocres:{:<8} complete:{:.0%}".format(score, (score/(num_eg * 100))))

        然后就去问学长看看实际工程环境是怎么解决这个问题的,然后得到了一个处理思路,于是写了一下,还是有一点问题,补救一下,perfect!

def is_chinese(uchar):
    # 判断当前字符是否为中文字符
    return uchar >= u'\u4e00' and uchar <= u'\u9fa5'


def func(ustring):
    # 将输入字符串调节为输出宽度为20个全角字符的宽度
    # 记录输入字符串的宽度,遇到中文字符加3,遇到其他字符加2
    num = 0
    for uchar in ustring:
        if is_chinese(uchar):
            num += 3
        else:
            num += 2
    # 计算需要补充的宽度,进行补充并返回补充后的字符串
    add = 60 - num
    if add % 2 == 0:
        return ustring + ' ' * int(add / 2)
    else:
        return ustring + '\u3000' + ' ' * int((add - 3) / 2)


for i in range(len(new_inf)):
    name = func(new_inf[i][0])
    score = int(new_inf[i][1])
    print("#{:<5} name:{:<20} ".format(i + 1, name), end="")
    print("\tsocres:{:<8} complete:{:.0%}".format(score, (score/(num_eg * 100))))

解决方案

具体的知识点(或者说是方案的思路)

        首先利用文档处理数据和基础的数据结构我不多说了,先说一下问题出现的原因。不论利用print的哪种格式化形式,其中格式化输出宽度指的是输出字符串的长度,而不是打印出来的视觉宽度(允许我使用这个词),但是我们知道python有好多字符格式,其中两个全角字符和三个半角字符视觉宽度相同(谁设的这个离谱参数),还有一些特殊字符视觉宽度只能以像素为单位进行计算(我也懒得数几个像素了),这就导致明明输出的一组字符串长度相同但是不对齐,所以一切的一切都是由于字符格式不统一。

        在意识到print格式化没用后,我先用了制表符和刷新符,然后太久没用导致了开始的乌龙。我的设想是前面不管了,直接设定后面的打印位置,当然我做游戏的经验告诉我如果是贴图的话这个思路是没问题的,不深究了。详见前面的三处错误。

        然后博客园有位大神(反正比我厉害)提供了一个思路,将所用字符全部转为全角格式,然后我就试了一下,这个注意将print格式化自动填充设为全角空格(也就是‘\u3000’),问题是解决了,但是看的很别扭。对了,这个要自学一下万维码,不是asc11码。

        关于制表这个其实是有第三方库的,但是这个效果一言难尽,可以自己搜索使用tabulate库。

        然后xzh学长提供思路:记录字符中各种字符的出现次数,然后预设自己的预期宽度,直接自己填充空格后返回就行,于是我的代码里面先预设了视觉宽度,然后遵循全角三格半角两格的原则减去,最后在返回的时候补上对应的空格,详见前方代码,这时候其实还是有问题的,因为特殊字符的视觉宽度真的就是以像素为单位的,加一个\t吧。

个人感悟

        打印小小一张表这么费劲,要不是处理的数据太多,榜单每天更新我真想手画。写下这篇博客记录我逝去的几个小时,不过进一步理解了字符的相关知识还是挺有意义的。

相关知识点:

1.print格式化输出

2.各类字符的显示问题

3.各类字符的编码关系

看到这里了,就点个赞吧,有问题就评论,说不定会回复。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值