在控制台应用程序 C 中读取/写入特殊字符(如波浪号、ñ、...)
Read / Write special characters (like tilde, ñ,...) in a console application C
我正在尝试让 C 控制台应用程序可以读取(使用键盘)特殊的西班牙字符,例如 scanf
或 gets
中的重音符、'ñ' 等,然后打印printf
.
感谢软件包 locale.h
,我已经实现了正确显示这些字符(存储在变量中或直接来自 printf
)。我举个例子:
#include <stdio.h>
// Add languaje package
#include <locale.h>
int main(void)
{
char string[254];
// Set languaje to Spanish
setlocale(LC_ALL, "spanish");
// Show correctly spanish special chars
printf("¡Success!. It is shown special chars like 'ñ' or 'á'.\n\n\n");
// Gets special chars by keyboard
printf("Input spanish special chars (such 'ñ'): ");
gets(string);
printf("Your string is: %s", string);
return 0;
}
但我还没有实现用上面提到的功能正确地拾取它们。
有人知道怎么做吗?
谢谢。
编辑 1:
在测试中,我观察到:
setlocale(LC_ALL, "spanish");
它正确显示了西班牙文的字符,但没有从键盘收集它们。
setlocale(LC_ALL, "es_ES");
它从键盘上正确地拾取西班牙文字符,但显示得不好。
编辑 2:
我也试过 setlocale(LC_ALL, "");
、setlocale(LC_ALL, "es_ES.UTF-8");
和 setlocale(LC_ALL, "es_ES.ISO_8859-15");
,结果与 EDIT 1 相同(或者从键盘上抓取字符或在控制台中很好地显示它们,但绝不能同时显示它们)。
Microsoft 的 C 运行时库 (CRT) 不支持 UTF-8 作为语言环境编码。它仅支持 Windows 代码页。此外,"es_ES" 不是有效的 CRT 语言环境字符串,因此 setlocale
会失败,使您处于默认的 C 语言环境。 Microsoft 的 CRT 的较新版本支持 Windows 语言环境名称,例如 "es-ES"(连字符,而不是下划线)。否则 CRT 使用全名或旧的 3 字母缩写,例如"spanish_spain"、"esp_esp" 或 "esp_esp.1252"。
但这还没有结束。当使用传统文本编码而不是 Unicode 读取和写入控制台时,控制台本身还有另一层翻译。为避免 mojibake,您必须设置控制台输入和输出代码页(即 SetConsoleCP
和 SetConsoleOutputCP
)以匹配语言环境代码页。如果您仅限于西班牙语或 Latin-1,那么将区域设置设置为 "spanish" 并通过 SetConsoleCP(1252)
和 SetConsoleOutputCP(1252)
设置控制台代码页应该可行。更一般地说,您可以查找给定语言环境名称的 ANSI 代码页,设置控制台代码页,并保存它们以便在退出时重置控制台。例如:
wchar_t *locale_name = L"es-ES";
if (_wsetlocale(LC_ALL, locale_name)) {
int codepage;
gPrevConsoleCP = GetConsoleCP();
if (gPrevConsoleCP) { // The process is attached to a console.
gPrevConsoleOutputCP = GetConsoleOutputCP();
if (GetLocaleInfoEx(locale_name,
LOCALE_IDEFAULTANSICODEPAGE |
LOCALE_RETURN_NUMBER,
(LPWSTR)&codepage,
sizeof(codepage) / sizeof(wchar_t))) {
if (!codepage) { // The locale doesn't have an ANSI codepage.
codepage = GetACP();
}
SetConsoleCP(codepage);
SetConsoleOutputCP(codepage);
atexit(reset_console);
}
}
}
也就是说,在使用控制台工作时,如果您将 stdin
和 stdout
设置为使用 _O_U16TEXT
模式并使用 wide-character 函数,通常情况会更好例如 fgetws
和 wprintf
。最终,如果 C 运行时库支持,这应该使用 wide-character 控制台 I/O 函数 ReadConsoleW
和 WriteConsoleW
。使用 UTF-16 wide-character 模式的缺点是它需要完全重写代码以使用 wchar_t
字符串和 wide-character 函数,并且还需要为使用的库实现适配器多字节编码字符串(最好是 UTF-8)。
我正在尝试让 C 控制台应用程序可以读取(使用键盘)特殊的西班牙字符,例如 scanf
或 gets
中的重音符、'ñ' 等,然后打印printf
.
感谢软件包 locale.h
,我已经实现了正确显示这些字符(存储在变量中或直接来自 printf
)。我举个例子:
#include <stdio.h>
// Add languaje package
#include <locale.h>
int main(void)
{
char string[254];
// Set languaje to Spanish
setlocale(LC_ALL, "spanish");
// Show correctly spanish special chars
printf("¡Success!. It is shown special chars like 'ñ' or 'á'.\n\n\n");
// Gets special chars by keyboard
printf("Input spanish special chars (such 'ñ'): ");
gets(string);
printf("Your string is: %s", string);
return 0;
}
但我还没有实现用上面提到的功能正确地拾取它们。
有人知道怎么做吗?
谢谢。
编辑 1:
在测试中,我观察到:
setlocale(LC_ALL, "spanish");
它正确显示了西班牙文的字符,但没有从键盘收集它们。setlocale(LC_ALL, "es_ES");
它从键盘上正确地拾取西班牙文字符,但显示得不好。
编辑 2:
我也试过 setlocale(LC_ALL, "");
、setlocale(LC_ALL, "es_ES.UTF-8");
和 setlocale(LC_ALL, "es_ES.ISO_8859-15");
,结果与 EDIT 1 相同(或者从键盘上抓取字符或在控制台中很好地显示它们,但绝不能同时显示它们)。
Microsoft 的 C 运行时库 (CRT) 不支持 UTF-8 作为语言环境编码。它仅支持 Windows 代码页。此外,"es_ES" 不是有效的 CRT 语言环境字符串,因此 setlocale
会失败,使您处于默认的 C 语言环境。 Microsoft 的 CRT 的较新版本支持 Windows 语言环境名称,例如 "es-ES"(连字符,而不是下划线)。否则 CRT 使用全名或旧的 3 字母缩写,例如"spanish_spain"、"esp_esp" 或 "esp_esp.1252"。
但这还没有结束。当使用传统文本编码而不是 Unicode 读取和写入控制台时,控制台本身还有另一层翻译。为避免 mojibake,您必须设置控制台输入和输出代码页(即 SetConsoleCP
和 SetConsoleOutputCP
)以匹配语言环境代码页。如果您仅限于西班牙语或 Latin-1,那么将区域设置设置为 "spanish" 并通过 SetConsoleCP(1252)
和 SetConsoleOutputCP(1252)
设置控制台代码页应该可行。更一般地说,您可以查找给定语言环境名称的 ANSI 代码页,设置控制台代码页,并保存它们以便在退出时重置控制台。例如:
wchar_t *locale_name = L"es-ES";
if (_wsetlocale(LC_ALL, locale_name)) {
int codepage;
gPrevConsoleCP = GetConsoleCP();
if (gPrevConsoleCP) { // The process is attached to a console.
gPrevConsoleOutputCP = GetConsoleOutputCP();
if (GetLocaleInfoEx(locale_name,
LOCALE_IDEFAULTANSICODEPAGE |
LOCALE_RETURN_NUMBER,
(LPWSTR)&codepage,
sizeof(codepage) / sizeof(wchar_t))) {
if (!codepage) { // The locale doesn't have an ANSI codepage.
codepage = GetACP();
}
SetConsoleCP(codepage);
SetConsoleOutputCP(codepage);
atexit(reset_console);
}
}
}
也就是说,在使用控制台工作时,如果您将 stdin
和 stdout
设置为使用 _O_U16TEXT
模式并使用 wide-character 函数,通常情况会更好例如 fgetws
和 wprintf
。最终,如果 C 运行时库支持,这应该使用 wide-character 控制台 I/O 函数 ReadConsoleW
和 WriteConsoleW
。使用 UTF-16 wide-character 模式的缺点是它需要完全重写代码以使用 wchar_t
字符串和 wide-character 函数,并且还需要为使用的库实现适配器多字节编码字符串(最好是 UTF-8)。