在Mac上用汉字命令调用popen()

Call popen() on a command with Chinese characters on Mac

本文关键字:调用 popen 命令 汉字 Mac      更新时间:2023-10-16

我正试图在Mac上使用popen()命令在文件上执行程序。为此,我创建了一个形式为<path-to_executable> <path-to-file>的命令,然后对该命令调用popen()。现在,这两个组件都是用char*声明的。我需要读取命令的输出,所以我需要popen()给出的管道。

现在,文件的路径可以包含中文、日语、俄语和几乎任何其他字符。为此,我可以将文件的路径表示为wchar_t*。但这对popen()不起作用,因为显然Mac/Linux不像Windows那样有一个宽的_wpopen(。

我还有其他方法可以做到这一点吗?我从一个只能给我wchar_t*的数据结构中获取文件的路径,所以我必须从那里获取它,并在需要时进行适当的转换。

提前谢谢。

编辑:

似乎有一天你会把头发拔出来。

因此,我尝试使用wcstomb,但对"C.UTF-8"及其任何排列的setlocale调用都失败了。不出所料,wcstobbs调用失败,之后返回-1。

然后,我尝试根据在谷歌上搜索的一些示例代码编写自己的iconv实现。我想出了一个顽固地拒绝工作的办法:

iconv_t cd = iconv_open("UTF-8", "WCHAR_T");
// error checking here
wchar_t* inbuf = ...; // get wchar_t* here
char outbuf[<size-of-inbuf>*4+1];
size_t inlen  = <size-of-inbuf>;
size_t outlen = <size-of-inbuf>*4+1;
char* c_inbuf  = (char*) inbuf;
char* c_outbuf = outbuf;
int ret = iconv(cd, &c_inbuf, &inlen, &c_outbuf, &outlen);
// more error checking here

iconv总是返回-1,并且errno设置为EINVAL。我已验证<size-of-len>设置正确。我不知道为什么这个代码现在失败了。

编辑2:

iconv失败了,因为我没有正确设置输入缓冲区长度。此外,Mac似乎不支持"WCHAR_t"编码,所以我将其更改为UTF-16。现在我已经更正了长度并更改了编码,但iconv只是返回,没有转换任何字符。它只返回0。

为了调试这个问题,我甚至将输入字符串更改为临时字符串,并适当地设置输入长度。即使这个iconv调用也只返回0。我的代码现在看起来像:

iconv_t cd = iconv_open("UTF-8", "UTF-16");
// error checking here
wchar_t* inbuf = ...; // get wchar_t* here - guaranteed to be UTF-16
char outbuf[<size-of-inbuf>*4+1];
size_t inlen  = <size-of-inbuf>;
size_t outlen = <size-of-inbuf>*4+1;
char* c_inbuf  = "abc"; // (char*) inbuf;
inlen = 4;
char* c_outbuf = outbuf;
int ret = iconv(cd, &c_inbuf, &inlen, &c_outbuf, &outlen);
// more error checking here

我已经确认转换器描述符被正确打开。from编码正确。输入缓冲区包含一些简单的字符。所有内容都是硬编码的,并且是静态的,iconv不转换任何字符,只返回0,而outbuf保持为空。

健康损失警报

popen需要一个UTF-8字符串。为此,可以使用iconv在不同编码之间进行转换,包括从本地wchar_t编码转换为UTF-8。(请注意,在我的Mac OS安装中,wchar_t实际上是32位,而不是16位。)

EDIT下面是一个在OS X Lion上运行的示例。我在使用wchar_t编码时没有遇到问题(iconv手册页中记录了这一点)。

#include <sys/param.h>
#include <string.h>
#include <iconv.h>
#include <stdio.h>
#include <errno.h>
char* utf8path(const wchar_t* wchar, size_t utf32_bytes)
{
    char result_buffer[MAXPATHLEN];
    iconv_t converter = iconv_open("UTF-8", "wchar_t");
    char* result = result_buffer;
    char* input = (char*)wchar;
    size_t output_available_size = sizeof result_buffer;
    size_t input_available_size = utf32_bytes;
    size_t result_code = iconv(converter, &input, &input_available_size, &result, &output_available_size);
    if (result_code == -1)
    {
        perror("iconv");
        return NULL;
    }
    iconv_close(converter);
    return strdup(result_buffer);
}
int main()
{
    wchar_t hello_world[] = L"/éè/path/to/hello/world.txt";
    char* utf8 = utf8path(hello_world, sizeof hello_world);
    printf("%sn", utf8);
    free(utf8);
    return 0;
}

utf8_hello_world函数接受字节长度wchar_t字符串,并返回等效的UTF-8字符串。如果处理指向wchar_t的指针而不是指向wchar_t的数组,则需要使用(wcslen(ptr) + 1) * sizeof(wchar_t)而不是sizeof

Mac OS X使用UTF-8,因此需要将宽字符串转换为UTF-8。您可以使用wcstombs来完成此操作,前提是您首先切换到UTF-8区域设置。例如:

// Do this once at program startup
setlocale(LC_ALL, "en_US.UTF-8");
...
// Error checking omitted for expository purposes
wchar_t *wideFilename = ...;  // This comes from wherever
char filename[256];  // Make sure this buffer is big enough!
wcstombs(filename, wideFilename, sizeof(filename));
// Construct popen command using the UTF-8 filename

如果您不想更改程序的区域设置,也可以使用libiconv为您进行UTF-16到UTF-8的转换;您也可以推出自己的实现,因为转换并不那么复杂。