为了账号安全,请及时绑定邮箱和手机立即绑定

我该如何处理这些奇怪的特殊字符弄乱了我的打印格式?

我该如何处理这些奇怪的特殊字符弄乱了我的打印格式?

杨魅力 2021-11-16 14:33:52
我正在打印一个格式化的表格。但有时这些用户生成的字符会占用不止一个字符的宽度,并且会弄乱格式,正如您在下面的屏幕截图中看到的那样......“标题”列的宽度被格式化为 68 字节。但是这些“特殊字符”占用了超过 1 个字符的宽度,但仅计为 1 个字符。这会将列推过其边界。print('{0:16s}{3:<18s}{1:68s}{2:>8n}'.format((    ' ' + streamer['user_name'][:12] + '..') if len(streamer['user_name']) > 12 else ' ' + streamer['user_name'],    (streamer['title'].strip()[:62] + '..') if len(streamer['title']) > 62 else streamer['title'].strip(),    streamer['viewer_count'],    (gamesDic[streamer['game_id']][:15] + '..') if len(gamesDic[streamer['game_id']]) > 15 else gamesDic[streamer['game_id']]))关于如何处理这些特殊字符的任何建议?编辑: 我将有问题的字符串打印到文件中。🔴𝐀𝐒𝐌𝐑 (𝙪𝙥 𝙘𝙡𝙤𝙨𝙚) ✨ LIVE 🔔 SUBS GET SNAPCHAT编辑2:为什么这些不在字符边界上对齐?
查看完整描述

2 回答

?
跃然一笑

TA贡献1826条经验 获得超6个赞

我对这个问题发表了以下评论:

“LIVE”中的字符为全角字符。一种处理它们的hacky方法可能是测试它们的宽度 unicodedata.east_asian_width(char)(对于全角字符它将返回“F”)并替换为的最后一个字符 unicodedata.name(char)(或仅将它们计为长度2)

这个“答案”本质上是另一个评论,但对于评论字段来说太长了。

这个 hack - 在 Alderven 的回答中实现- 几乎适用于 OP,但示例字符串以额外的半个字符宽度呈现(注意示例字符串不包含任何东亚半角字符。)。

我无法使用此测试语句重现此确切行为,s问题中的示例字符串在哪里,更改删除的字符:

print((s + (68 - (len(s) + sum(1 for x in s if ud.east_asian_width(x) in ('F', 'N', 'W')))) * 'x')+ '\n'+ ('x' * 68))

在 Debian 的 Gnome 终端中的 Python 3.6 解释器中,使用默认的等宽常规字体,删除全角字符会导致示例字符串明显比等效的 "x" 字符串长三个字符。

删除全角和宽(东亚宽度“W”)字符生成的字符串似乎呈现与等效数量的“x”相同的长度。

在 OpenSuse 上的 Python 3.7 KDE Konsole 终端中,使用 Ubuntu Monospace 常规字体,我无法生成呈现相同长度的字符串,无论我删除了全角、宽角或中性 (“N”) 字符的组合如何。

我确实注意到,在 Konsole 中单独渲染时,火花字符 (✨) 似乎占用了额外的半宽,但在测试完整字符串时看不到任何半宽差异。

我怀疑问题出在 Python 控制之外的低级渲染,正如关于 Unicode标准的这个注释所暗示的那样:

注意: East_Asian_Width 属性不适用于现代终端仿真器,而无需根据具体情况进行适当调整。此类终端仿真器需要一种方法来解决此类环境所需的半角/全角二分法,但 East_Asian_Width 属性并未为所有情况提供现成的解决方案。Unicode 标准不断增长的曲目早已超出了东亚传统字符编码的范围,终端仿真通常需要定制以支持边缘情况和随着时间的推移排版行为的变化。


查看完整回答
反对 回复 2021-11-16
?
梦里花落0921

TA贡献1772条经验 获得超6个赞

我已经根据@snakecharmerb 的评论编写了自定义字符串格式化程序,但仍然存在“半字符宽度”问题:


import unicodedata


def fstring(string, max_length, align='l'):

    string = str(string)

    extra_length = 0

    for char in string:

        if unicodedata.east_asian_width(char) == 'F':

            extra_length += 1


    diff = max_length - len(string) - extra_length

    if diff > 0:

        return string + diff * ' ' if align == 'l' else diff * ' ' + string

    elif diff < 0:

        return string[:max_length-3] + '.. '


    return string


data = [{'user_name': 'shroud', 'game_id': 'Apex Legends', 'title': 'pathfinder twitch prime loot YAYA @shroud on socials for update', 'viewer_count': 66200},

        {'user_name': 'Amouranth', 'game_id': 'ASMR', 'title': '🔴 𝐀𝐒𝐌𝐑 (𝙪𝙥 𝙘𝙡𝙤𝙨𝙚) ✨ LIVE 🔔 SUBS GET SNAPCHAT', 'viewer_count': 2261}]


for d in data:

    name = fstring(d['user_name'], 20)

    game_id = fstring(d['game_id'], 15)

    title = fstring(d['title'], 62)

    count = fstring(d['viewer_count'], 10, align='r')

    print('{}{}{}{}'.format(name, game_id, title, count))

它产生输出:

//img1.sycdn.imooc.com//619351160001a0d707660032.jpg

查看完整回答
反对 回复 2021-11-16
  • 2 回答
  • 0 关注
  • 192 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信