接受非 ASCII 字符
Accept non ASCII characters
考虑这个程序:
#include <stdio.h>
int main(int argc, char* argv[]) {
printf("%s\n", argv[1]);
return 0;
}
我是这样编译的:
x86_64-w64-mingw32-gcc -o alpha alpha.c
问题是如果我给它一个非 ASCII 参数:
$ ./alpha róisín
r�is�n
我如何编写 and/or 编译该程序使其接受非 ASCII
人物?回复alk: 不是,程序打印错误。看
这个例子:
$ echo Ω | od -t x1c
0000000 ce a9 0a
316 251 \n
0000003
$ ./alpha Ω | od -t x1c
0000000 4f 0d 0a
O \r \n
0000003
尝试编译并运行安装以下程序:
#include <stdio.h>
int main()
{
int i = 0;
for( i=0; i<256; i++){
printf("\nASCII Character #%d:%c ", i, i);
}
printf("\n");
return 0;
}
在您的输出中,您应该会看到从数字 128 开始的那些小问号。仅供参考,我正在使用 Ubuntu,当我编译和 运行 这个程序(使用 GNOME 终端)时,我也会遇到这种情况。
但是,如果我转到终端 > 设置字符编码...和 select 西方 (WINDOWS-1252) 而不是 Unicode (UTF-8),然后重新 运行程序,扩展ASCII字符显示正常
我不知道 Windows/MinGW 的确切步骤,但是,简而言之,更改字符编码应该可以解决您的问题.
由于您使用的是 MinGW(实际上是 MinGW-w64,但在本例中这无关紧要),您可以访问 Windows API,因此以下内容适用于你。它可能更干净并且经过实际测试,但它至少应该提供一个好主意:
#define _WIN32_WINNT 0x0600
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <windows.h>
int main (void)
{
int argc;
int i;
LPWSTR *argv;
argv = CommandLineToArgvW(GetCommandLineW(), &argc);
if (argv == NULL)
{
FormatMessageA(
(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS),
NULL,
GetLastError(),
0,
(LPWSTR)&error, 0,
NULL);
fprintf(stderr, error);
fprintf(stderr, "\n");
LocalFree(error);
return EXIT_FAILURE;
}
for (i = 0; i < argc; ++i)
wprintf(L"argv[%d]: %ls\n", i, argv[i]);
// You must free argv using LocalFree!
LocalFree(argv);
return 0;
}
请记住这个问题:Windows 不会为您编写字符串。我使用自己的 Windows 键盘布局,它使用组合字符(我很奇怪),所以当我键入
example -o àlf
在我的 Windows 命令提示符中,我得到以下输出:
argv[0]: example
argv[1]: -o
argv[2]: a\u0300lf
a\u0300
是 U+0061 (LATIN SMALL LETTER A)
后跟 Unicode 代码点的表示形式 U+0300 (COMBINING GRAVE ACCENT)
。如果我改用
example -o àlf
使用预组合字符 U+00E0 (LATIN SMALL LETTER A WITH GRAVE)
,输出会有所不同:
argv[0]: example
argv[1]: -o
argv[2]: \u00E0lf
其中 \u00E0
是 Unicode 代码点 U+00E0 表示的预组合字符 à
的表示。然而,虽然我可能是一个奇怪的人,但 Vietnamese code page 1258 实际上包括组合字符。这应该不会影响通常的文件名处理,但可能会遇到一些困难。
对于只是字符串的参数,您可能需要使用 NormalizeString
函数研究规范化。其中链接的文档和示例应该可以帮助您了解该功能的工作原理。 Unicode 中的规范化和其他一些事情可能是一段漫长的旅程,但如果这类事情让您兴奋,那也是一段有趣的旅程。
最简单的方法是 wmain
:
#include <fcntl.h>
#include <stdio.h>
int wmain (int argc, wchar_t** argv) {
_setmode(_fileno(stdout), _O_WTEXT);
wprintf(L"%s\n", argv[1]);
return 0;
}
也可以用GetCommandLineW
来完成;这是代码的简单版本
发现于 HandBrake repo:
#include <stdio.h>
#include <windows.h>
int get_argv_utf8(int* argc_ptr, char*** argv_ptr) {
int argc;
char** argv;
wchar_t** argv_utf16 = CommandLineToArgvW(GetCommandLineW(), &argc);
int i;
int offset = (argc + 1) * sizeof(char*);
int size = offset;
for (i = 0; i < argc; i++)
size += WideCharToMultiByte(CP_UTF8, 0, argv_utf16[i], -1, 0, 0, 0, 0);
argv = malloc(size);
for (i = 0; i < argc; i++) {
argv[i] = (char*) argv + offset;
offset += WideCharToMultiByte(CP_UTF8, 0, argv_utf16[i], -1,
argv[i], size-offset, 0, 0);
}
*argc_ptr = argc;
*argv_ptr = argv;
return 0;
}
int main(int argc, char** argv) {
get_argv_utf8(&argc, &argv);
printf("%s\n", argv[1]);
return 0;
}
考虑这个程序:
#include <stdio.h>
int main(int argc, char* argv[]) {
printf("%s\n", argv[1]);
return 0;
}
我是这样编译的:
x86_64-w64-mingw32-gcc -o alpha alpha.c
问题是如果我给它一个非 ASCII 参数:
$ ./alpha róisín
r�is�n
我如何编写 and/or 编译该程序使其接受非 ASCII 人物?回复alk: 不是,程序打印错误。看 这个例子:
$ echo Ω | od -t x1c
0000000 ce a9 0a
316 251 \n
0000003
$ ./alpha Ω | od -t x1c
0000000 4f 0d 0a
O \r \n
0000003
尝试编译并运行安装以下程序:
#include <stdio.h>
int main()
{
int i = 0;
for( i=0; i<256; i++){
printf("\nASCII Character #%d:%c ", i, i);
}
printf("\n");
return 0;
}
在您的输出中,您应该会看到从数字 128 开始的那些小问号。仅供参考,我正在使用 Ubuntu,当我编译和 运行 这个程序(使用 GNOME 终端)时,我也会遇到这种情况。
但是,如果我转到终端 > 设置字符编码...和 select 西方 (WINDOWS-1252) 而不是 Unicode (UTF-8),然后重新 运行程序,扩展ASCII字符显示正常
我不知道 Windows/MinGW 的确切步骤,但是,简而言之,更改字符编码应该可以解决您的问题.
由于您使用的是 MinGW(实际上是 MinGW-w64,但在本例中这无关紧要),您可以访问 Windows API,因此以下内容适用于你。它可能更干净并且经过实际测试,但它至少应该提供一个好主意:
#define _WIN32_WINNT 0x0600
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <windows.h>
int main (void)
{
int argc;
int i;
LPWSTR *argv;
argv = CommandLineToArgvW(GetCommandLineW(), &argc);
if (argv == NULL)
{
FormatMessageA(
(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS),
NULL,
GetLastError(),
0,
(LPWSTR)&error, 0,
NULL);
fprintf(stderr, error);
fprintf(stderr, "\n");
LocalFree(error);
return EXIT_FAILURE;
}
for (i = 0; i < argc; ++i)
wprintf(L"argv[%d]: %ls\n", i, argv[i]);
// You must free argv using LocalFree!
LocalFree(argv);
return 0;
}
请记住这个问题:Windows 不会为您编写字符串。我使用自己的 Windows 键盘布局,它使用组合字符(我很奇怪),所以当我键入
example -o àlf
在我的 Windows 命令提示符中,我得到以下输出:
argv[0]: example
argv[1]: -o
argv[2]: a\u0300lf
a\u0300
是 U+0061 (LATIN SMALL LETTER A)
后跟 Unicode 代码点的表示形式 U+0300 (COMBINING GRAVE ACCENT)
。如果我改用
example -o àlf
使用预组合字符 U+00E0 (LATIN SMALL LETTER A WITH GRAVE)
,输出会有所不同:
argv[0]: example
argv[1]: -o
argv[2]: \u00E0lf
其中 \u00E0
是 Unicode 代码点 U+00E0 表示的预组合字符 à
的表示。然而,虽然我可能是一个奇怪的人,但 Vietnamese code page 1258 实际上包括组合字符。这应该不会影响通常的文件名处理,但可能会遇到一些困难。
对于只是字符串的参数,您可能需要使用 NormalizeString
函数研究规范化。其中链接的文档和示例应该可以帮助您了解该功能的工作原理。 Unicode 中的规范化和其他一些事情可能是一段漫长的旅程,但如果这类事情让您兴奋,那也是一段有趣的旅程。
最简单的方法是 wmain
:
#include <fcntl.h>
#include <stdio.h>
int wmain (int argc, wchar_t** argv) {
_setmode(_fileno(stdout), _O_WTEXT);
wprintf(L"%s\n", argv[1]);
return 0;
}
也可以用GetCommandLineW
来完成;这是代码的简单版本
发现于 HandBrake repo:
#include <stdio.h>
#include <windows.h>
int get_argv_utf8(int* argc_ptr, char*** argv_ptr) {
int argc;
char** argv;
wchar_t** argv_utf16 = CommandLineToArgvW(GetCommandLineW(), &argc);
int i;
int offset = (argc + 1) * sizeof(char*);
int size = offset;
for (i = 0; i < argc; i++)
size += WideCharToMultiByte(CP_UTF8, 0, argv_utf16[i], -1, 0, 0, 0, 0);
argv = malloc(size);
for (i = 0; i < argc; i++) {
argv[i] = (char*) argv + offset;
offset += WideCharToMultiByte(CP_UTF8, 0, argv_utf16[i], -1,
argv[i], size-offset, 0, 0);
}
*argc_ptr = argc;
*argv_ptr = argv;
return 0;
}
int main(int argc, char** argv) {
get_argv_utf8(&argc, &argv);
printf("%s\n", argv[1]);
return 0;
}