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

时间:2019-02-22 07:04:53

标签: python unicode terminal string-formatting python-unicode

我正在打印格式化的表格。但是有时这些用户生成的字符占用多个字符宽度,并且弄乱了格式,如下面的屏幕截图所示...

enter image description here

“标题”列的宽度格式为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']]))

关于如何处理这些特殊字符的任何建议?

编辑:我将有问题的字符串打印到文件中。

  

()L LIVE SUBS GET SNAPCHAT

edit2

为什么这些字符不能在字符边界上对齐?

enter image description here

edit3:

今天,前两个字符产生了奇怪的输出。但是在下面的每种情况下,列都是对齐的。

第一个孤立的字符...

title[0]

enter image description here

孤立的第二个字符... title[1]

enter image description here

第一个和第二个字符在一起。.title[0] + title[1]

first and second character

2 个答案:

答案 0 :(得分:2)

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

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))

它产生输出:

enter image description here

(由于格式会丢失,因此无法将其发布为文本)

答案 1 :(得分:2)

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

  

“ LIVE”中的字符是Fullwidth个字符。一种骇人听闻的方式   处理它们可能是用   unicodedata.east_asian_width(char)(全角返回“ F”   字符)并替换为的最终字符   unicodedata.name(char)(或仅将其视为长度2)

此“答案”实质上是另一条评论,但对于评论字段而言太长了。

按照Alderven的answer的实现,此hack几乎适用于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 standard上的注释说明:

  

注意:East_Asian_Width属性不适用于现代人   终端仿真器,没有根据具体情况进行适当调整   基础。此类终端仿真器需要一种方法来解决   在这种环境下,必须使用半角/全角二分法,   但是East_Asian_Width属性没有提供现成的   适用于所有情况的解决方案。 Unicode的曲目越来越多   标准早已超越了东亚传统人物的界限   编码和终端仿真通常需要自定义为   支持极端情况和印刷行为的变化   时间。

相关问题