在Mac上用中文字符命令调用popen()

时间:2012-02-02 18:11:48

标签: c++ macos encoding popen utf-16

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

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

有没有其他方法可以让我的工作?我从一个只能给我wchar_t *的数据结构中获取路径到文件,所以我必须从那里获取并根据需要适当地转换它。

提前致谢。

修改

似乎有一天你最后拔头发。

所以我尝试使用wcstombs,但是对于“C.UTF-8”及其任何排列,setlocale调用都失败了。不出所料,wcstombs调用失败后返回-1。

然后我尝试根据在Google上搜索的一些示例代码编写自己的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

我已确认转换器描述符正在正确打开。从编码是正确的。输入缓冲区包含一些简单字符。一切都是硬编码的,而且,iconv不会转换任何字符,只返回0并且outbuf仍为空。

完全失去警惕!

2 个答案:

答案 0 :(得分:1)

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

编辑以下是适用于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("%s\n", utf8);
    free(utf8);
    return 0;
}

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

答案 1 :(得分:0)

Mac OS X使用UTF-8,因此您需要将宽字符串转换为UTF-8。如果您首先切换到UTF-8语言环境,则可以使用wcstombs执行此操作。例如:

// 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转换;你也可以推出自己的实现,因为转换并不是那么复杂。