从使用 fmemopen 创建的流中读取宽字符

Read wide char from a stream created with fmemopen

我正在尝试从使用 fmemopenchar * 创建的流中读取宽字符。

char *s = "foo bar foo";
FILE *f = fmemopen(s,strlen(s),"r");

wchar_t c = getwc(f);

getwc 抛出一个分段错误,我用 GDB 检查过。

我知道这是因为使用 fmemopen 打开了流,因为在打开的流上调用 getwc 正常工作。

是否有 fmemopen 的宽字符版本,或者是否有其他方法可以解决此问题?

  1. 你的第二行没有使用正确数量的参数,是吗?已更正

    FILE *fmemopen(void *buf, size_t size, const char *mode);

  2. glibc 的 fmemopen(完全) 支持宽字符 AFAIK。还有open_wmemstream(),支持宽字符,只是为了写。

  3. 是否定义了_UNICODE?参见 wchar_t reading
    另外,您是否将语言环境设置为支持 Unicode 的编码,例如 setlocale(LC_ALL, "en_US.UTF-8");?参见 here

  4. 考虑使用临时 file. Consider using fgetwc / 4

我更改了我的代码并采用了@chqrlie 的代码,因为它更接近 OP 代码但添加了语言环境,否则它无法为 extended/Unicode 个字符生成正确的输出。

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include <stdlib.h>
#include <locale.h>

int main(void)
{
    setlocale(LC_ALL, "en_US.UTF-8");
    const char *s = "foo $€ bar foo";
    FILE *f = fmemopen((void *)s, strlen(s), "r");
    wchar_t c;

    if (f == NULL) {
        printf("fmemopen failed: %s\n", strerror(errno));
        return 1;
    }
    printf("default wide orientation: %d\n", fwide(f, 0));
    printf("selected wide orientation: %d\n", fwide(f, 1));
    while ((c = getwc(f)) != WEOF) {
        printf("read %lc (%d 0x%x)\n", c, c, c);
    }
    return 0;
}

第二行应显示为 FILE *f = fmemopen(s, strlen(s), "r");。如发布的那样,fmemopen 具有未定义的行为并且可能 return NULL,这会导致 getwc() 崩溃。

更改 fmemopen() 行并添加对 NULL 的检查修复了崩溃问题,但不符合 OP 目标。

fmemopen() 打开的流似乎不支持宽方向,至少对于 GNU C 库是这样。请注意,fmemopen 未在 C 标准中定义,而是在 POSIX.1-2008 中定义,并且在许多系统上不可用(如 OS/X)。

这是您程序的更正和扩展版本:

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>

int main(void) {
    const char *s = "foo bar foo";
    FILE *f = fmemopen((void *)s, strlen(s), "r");
    wchar_t c;

    if (f == NULL) {
        printf("fmemopen failed: %s\n", strerror(errno));
        return 1;
    }
    printf("default wide orientation: %d\n", fwide(f, 0));
    printf("selected wide orientation: %d\n", fwide(f, 1));
    while ((c = getwc(f)) != WEOF) {
        printf("read %lc (%d 0x%x)\n", c, c, c);
    }
    return 0;
}

运行 在 linux:

default wide orientation: -1
selected wide orientation: -1

无输出,WEOF 立即 returned。

linux 手册页中对 fwide(f, 0) 的解释:

SYNOPSIS

#include <wchar.h>
int fwide(FILE *stream, int mode);

When mode is zero, the fwide() function determines the current orientation of stream. It returns a positive value if stream is wide-character oriented, that is, if wide-character I/O is permitted but char I/O is disallowed. It returns a negative value if stream is byte oriented, i.e., if char I/O is permitted but wide-character I/O is disallowed. It returns zero if stream has no orientation yet; in this case the next I/O operation might change the orientation (to byte oriented if it is a char I/O operation, or to wide-character oriented if it is a wide-character I/O operation).

Once a stream has an orientation, it cannot be changed and persists until the stream is closed.

When mode is nonzero, the fwide() function first attempts to set stream's orientation (to wide-character oriented if mode is greater than 0, or to byte oriented if mode is less than 0). It then returns a value denoting the current orientation, as above.

fmemopen() 编辑的流 return 是面向字节的,不能更改为面向宽字符。

  1. 您只能在无向或宽向流上使用 getwc()。来自 getwc() man page: 流应该还没有方向,或者是宽方向的。

  2. 如果流已经有方向,则无法更改流方向。来自 fwide() man page在已经有方向的流上调用此函数无法更改它。

  3. 用 glibc 的 fmemopen() 打开的流具有字节方向,因此不能以任何方式面向宽。正如所描述的 here uClibcfmemopen() 没有这个限制的例程。

结论:您需要使用 uClibc 或其他库或自己制作 fmemopen()