如何在Python的curses库中使用扩展字符?
我最近在看关于Python中Curses编程的教程,很多教程提到可以使用扩展字符,比如画线的符号。这些字符的编码值大于255,而curses库知道如何在当前终端字体中显示它们。
有些教程说你可以这样使用:
c = ACS_ULCORNER
...还有一些说你应该这样用:
c = curses.ACS_ULCORNER
(这应该是一个框的左上角,看起来像一个上下翻转的L)
无论我用哪种方法,程序总是提示名称未定义,因此运行失败。我试过“import curses”和“from curses import *”,但都不行。
Curses的window()函数会用到这些字符,所以我甚至试着在我的电脑上找源代码,看看它是怎么做的,但我找了半天也没找到。
3 个回答
我觉得下面的内容和这个问题是相关的,可以发在这里。我将使用utfinfo.pl(也可以在Super User上查看)。
首先,对于标准的ASCII字符集,Unicode代码点和字节编码是一样的:
$ echo 'a' | perl utfinfo.pl
Char: 'a' u: 97 [0x0061] b: 97 [0x61] n: LATIN SMALL LETTER A [Basic Latin]
所以我们可以在Python的curses
中这样做:
window.addch('a')
window.border('a')
...这样就能按预期工作了。
但是,如果一个字符超出了基本的ASCII范围,那么就会有一些不同之处,这一点在addch
的文档中并没有明确说明。首先,我可以这样做:
window.addch(curses.ACS_PI)
window.border(curses.ACS_PI)
...在这种情况下,在我的gnome-terminal
中,Unicode字符'π'会被显示出来。不过,如果你查看ACS_PI
,你会发现它是一个整数,值为4194427(0x40007b);所以下面的代码也会显示同样的字符(或者说是字形?)'π':
window.addch(0x40007b)
window.border(0x40007b)
为了搞清楚发生了什么,我在ncurses
的源代码中查找,发现了以下内容:
#define ACS_PI NCURSES_ACS('{') /* Pi */
#define NCURSES_ACS(c) (acs_map[NCURSES_CAST(unsigned char,c)])
#define NCURSES_CAST(type,value) static_cast<type>(value)
#lib_acs.c: NCURSES_EXPORT_VAR(chtype *) _nc_acs_map(void): MyBuffer = typeCalloc(chtype, ACS_LEN);
#define typeCalloc(type,elts) (type *)calloc((elts),sizeof(type))
#./widechar/lib_wacs.c: { '{', { '*', 0x03c0 }}, /* greek pi */
注意这里:
$ echo '{π' | perl utfinfo.pl
Got 2 uchars
Char: '{' u: 123 [0x007B] b: 123 [0x7B] n: LEFT CURLY BRACKET [Basic Latin]
Char: 'π' u: 960 [0x03C0] b: 207,128 [0xCF,0x80] n: GREEK SMALL LETTER PI [Greek and Coptic]
...这两个都和ACS_PI
的值4194427(0x40007b)没有关系。
因此,当addch
和/或border
遇到一个超出ASCII的字符(基本上是一个unsigned int
,而不是unsigned char
)时,它们(至少在这个例子中)并不是把这个数字当作Unicode代码点,或者UTF-8编码的字节表示,而是把它当作acs_map
查找函数的索引(不过最终它确实会返回Unicode代码点,即使它模拟的是VT-100)。这就是为什么下面的代码:
window.addch('π')
window.border('π')
在Python 2.7中会失败,提示argument 1 or 3 must be a ch or an int
;而在Python 3.2中则会直接显示一个空格,而不是字符。当我们指定'π'
时,实际上我们指定的是UTF-8编码[0xCF,0x80] - 但即使我们指定Unicode代码点:
window.addch(0x03C0)
window.border0x03C0)
...在Python 2.7和3.2中都不会显示任何东西(空格)。
话虽如此,addstr
函数确实可以接受UTF-8编码的字符串,并且工作正常:
window.addstr('π')
...但是对于边框来说,由于border()
显然以和addch()
相同的方式处理字符 - 我们显然没办法处理那些没有明确指定为ACS
常量的字符(而且这些常量也不多)。
希望这对某些人有帮助,
祝好!
你需要把你的本地设置为“全部”,然后把输出编码成utf-8,方法如下:
import curses
import locale
locale.setlocale(locale.LC_ALL, '') # set your locale
scr = curses.initscr()
scr.clear()
scr.addstr(0, 0, u'\u3042'.encode('utf-8'))
scr.refresh()
# here implement simple code to wait for user input to quit
scr.endwin()
输出结果: あ
来自 curses/__init__.py
:
有一些常量,特别是那些以
ACS_*
开头的常量,只有在调用了initscr()
这个函数之后,才会被添加到 C 语言的_curses
模块的字典里。(有些版本的 SGI 的 curses 在调用initscr()
之前并不会给这些常量定义值。)这个包装函数会先调用底层的 C 语言的initscr()
,然后再把_curses
模块中的常量复制到 curses 包的字典里。如果你需要使用ACS_*
常量,最好不要用 'from curses import *
' 这种方式。
换句话说:
>>> import curses
>>> curses.ACS_ULCORNER
exception
>>> curses.initscr()
>>> curses.ACS_ULCORNER
>>> 4194412