【发布时间】:2018-09-20 16:37:31
【问题描述】:
我用 Ruby 开发一个游戏。它使用 ncurses 绘制窗口内容并刷新屏幕。这一切都在我开发这些东西的 macOS 上运行良好。但在 Linux 上,当与来自 Ruby 或纯 C 的 ncurses 接口时,除了纯 ASCII 之外,我无法打印任何东西。
例如,我得到这个输出:
Hello, world! M-b~U~TM-b~U~PM-b~U~WM-b~U~QM-b~U~QM-b~U~ZM-b~U~PM-b~U~]M-b~U~_M-b~T~@M-b~UM-"
而不是我放入源代码的内容:
Hello, world! ╔═╗║║╚═╝╟─╢
文件编码为 UTF-8、env 或 locale 输出报告 LANG 和 LC_* 变量为 en_US 或 en_US.UTF-8 在适当的情况下。终端可以很好地打印这些字符,所以不是字体或终端模拟器设置。
Python 3 也可以正常工作。 Ruby 和 C 没有。
(在安装了libncurses5-dev 的全新 Linux Mint 19 设置上测试。)
那么,是我遗漏了一些设置,还是 Python 绑定有些特殊而我运气不佳?
它的样子
它应该看起来像这样(macOS):
但看起来确实像这样:
Python 代码
这段代码运行良好:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import locale
import curses
locale.setlocale(locale.LC_ALL, '')
stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
stdscr.addstr(0, 0, "╔═╗║║╚═╝╟─╢") # dont even need .encode('UTF-8')
stdscr.refresh()
stdscr.getkey()
curses.endwin()
curses 模块的文档说明你必须从 ncurses 5 开始执行此操作:https://docs.python.org/3/library/curses.html
从 5.4 版开始,ncurses 库决定如何使用 nl_langinfo 函数解释非 ASCII 数据。这意味着您必须在应用程序中调用 locale.setlocale() 并使用系统可用的编码之一对 Unicode 字符串进行编码。
Ruby 代码
# coding: utf-8
require "curses"
Curses.init_screen
Curses.start_color
Curses.stdscr.keypad(true) # enable arrow keys
Curses.cbreak # no line buffering / immediate key input
Curses.ESCDELAY = 0
Curses.curs_set(0) # Invisible cursor
Curses.noecho # Do not print keyboard input
Curses.stdscr.addstr(STYLES[:single])
Curses.stdscr.setpos(2,0)
Curses.stdscr.addstr(%Q{╔═╗║║╚═╝╟─╢})
Curses.getch
尝试使用 ffi-ncurses gem 而不是 curses 但无济于事。输出是一样的。
C 代码
我用编译
gcc -finput-charset=UTF-8 -fexec-charset=UTF-8 -pedantic -Wall -o main main.c -lncurses
代码如下:
// Most of the code is sample code from "CURHELLO.C"
// (c) Copyright Paul Griffiths 1999
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <langinfo.h>
#include <locale.h>
#include <ncurses.h>
int main(void) {
// Here I tried to copy what Python is doing:
setlocale(LC_ALL, ""); // Also tried "C.UTF-8", "en_US.UTF-8"
nl_langinfo(CODESET);
WINDOW * mainwin;
if ( (mainwin = initscr()) == NULL ) {
fprintf(stderr, "Error initialising ncurses.\n");
exit(EXIT_FAILURE);
}
start_color();
clear();
cbreak();
noecho();
keypad(stdscr, TRUE);
mvaddstr(1, 1, "Hello, world! ╔═╗║║╚═╝╟─╢");
refresh();
sleep(3);
delwin(mainwin);
endwin();
refresh();
return EXIT_SUCCESS;
}
编辑
通过链接到ncursesw(注意尾随的W!)使其正常工作,但它使用Ubuntu Mono(与我在OSX 上使用iTerm 尝试过的字体相同)在Linux 上以两倍的文本宽度显示特殊字符.
【问题讨论】:
-
将 C 代码与
ncursesw链接起来,它就可以工作了。 (虽然 ncursesw 为宽字符添加了新的类型和接口,但它也为标准的 ncurses 函数添加了 UTF-8 多字节支持。) -
此外,Ruby 应该更喜欢 ncursesw 而不是 ncurses,因此您可能没有安装 ncursesw 包。该库由 Debian、Ubuntu、Mint 上的
libncursesw5提供(您可能错过了这一点)。如果您还想尝试 C 中 ncursesw 中的其他接口,请同时安装libncursesw5-dev包。最后推荐大家使用gcc -Wall -O2 $(pkg-config --cflags ncursesw) main.c $(pkg-config --libs ncursesw) -o main编译C程序。 -
我刚刚检查过,并注意到块元素字符,如
▌▙▚█▞▟▐在 Ubuntu Monospace 中不是等宽的、Oxygen Mono、Noto Mono 或 Linux Libertine Mono O 字体:即使它们声称是等宽字体,但对于块元素来说并非如此!在我的系统上,Nimbus Mono L、Monospace、Liberation Mono、FreeMono、DejaVu Sans Mono、Bitstream Vera Sans Mono 和 Andale Mono 字体似乎具有适当的等宽字形,即使对于 Box Drawing 和 Block Elements,我建议您使用其中之一代替字体。 (并在您的文档中提及问题和字体!) -
您不能从终端内运行的程序更改字体;您必须指示用户更改它。对于 gnome-terminal,选择 Edit,然后选择 Profile preferences,然后选择 General 选项卡,选中 Custom font 复选框,然后然后使用复选框右侧的按钮选择字体。在 mate-terminal 中,它位于同一个选项卡中,但您需要取消选中 使用系统字体 才能选择字体。我从未使用过不允许我选择字体的 X 终端模拟器。
-
尝试在终端中运行
printf '0123456789012345678\n╔═╗║║╚═╝╟─╢▌▙▚█▞▟▐|\n',看看是否真的是字体问题。如果你问我,我会改用 SDL 或 GTK+3.0,使用 SVG 格式的图块。
标签: python c ruby linux ncurses