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

时间:2017-08-10 22:12:52

标签: c unicode stream segmentation-fault widechar

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

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

wchar_t c = getwc(f);

getwc抛出了分段错误,我使用GDB进行了检查。

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

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

3 个答案:

答案 0 :(得分:7)

第二行应为FILE *f = fmemopen(s, strlen(s), "r");。发布后,fmemopen有未定义的行为,可能会返回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

来自linux手册页的fwide(f, 0)的解释:

  

概要

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

mode为零时,fwide()函数会确定stream的当前方向。如果stream是面向宽字符的,即,如果允许宽字符I / O但不允许char I / O,则返回正值。如果stream是面向字节的,则返回负值,即,如果允许char I / O但不允许宽字符I / O.如果stream尚无方向,则返回零;在这种情况下,下一个I / O操作可能会改变方向(如果是char I / O操作,则改为字节,如果是宽字符I / O操作,则改为宽字符)。

     

一旦流具有方向,它就无法更改并持续直到关闭流。

     

mode非零时,fwide()函数首先尝试设置stream的方向(如果模式大于0,则为宽字符方向,或者为字节如果mode小于0,则导向。然后它返回一个表示当前方向的值,如上所述。

fmemopen()返回的流是面向字节的,不能更改为面向广角的。

答案 1 :(得分:3)

  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。请考虑改为使用fgetwc / 4

  5. 我已经更改了我的代码并采用了@chqrlie中的代码,因为它更接近OP代码但添加了语言环境,否则无法为扩展/ 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;
    }
    

答案 2 :(得分:1)

  1. 您只能在无定向或面向广播的流上使用getwc()。来自getwc() https://link.springer.com/article/10.1007/s10032-011-0175-3该流不具有方向,或者是广泛的。

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

  3. 使用glibc打开的流fmemopen()具有字节方向,因此无法以任何方式进行广泛定向。如上所述,man page here具有fmemopen()例程,没有此限制。

  4. 结论:您需要使用uClibc或其他图书馆或制作自己的fmemopen()