简体   繁体   中英

Read wide char from a stream created with fmemopen

I'm trying to read a wide char from a stream that was created using fmemopen with a char * .

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

wchar_t c = getwc(f);

getwc throws a segmentation fault, I checked using GDB.

I know this is due to opening the stream with fmemopen , because calling getwc on a stream opened normally works fine.

Is there a wide char version of fmemopen , or is there some other way to fix this problem?

The second line should read FILE *f = fmemopen(s, strlen(s), "r"); . As posted, fmemopen has undefined behavior and might return NULL , which causes getwc() to crash.

Changing the fmemopen() line and adding a check for NULL fixes the crash but does not meet the OPs goal.

It seems wide orientation is not supported on streams open with fmemopen() , At least for the GNU C library. Note that fmemopen is not defined in the C Standard but in POSIX.1-2008 and is not available on many systems (like OS/X).

Here is a corrected and extended version of your program:

#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;
}

Run on linux:

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

No output, WEOF is returned immediately.

Explanation for fwide(f, 0) from the linux man page:

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, ie, 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.

The stream returned by fmemopen() is byte-oriented and cannot be changed to wide-character oriented.

  1. Your second line does not use the correct number of parameters, does it? corrected

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

  2. glibc's fmemopen does not (fully) support wide characters AFAIK. There's also open_wmemstream() , which supports wide characters but is just for writing.

  3. Is _UNICODE defined? See wchar_t reading .
    Also , have you set the locale to an encoding that supports Unicode, for example, setlocale(LC_ALL, "en_US.UTF-8"); ? See here .

  4. Consider using a temporary file . Consider using fgetwc / 4 instead.

I have changed my code and adopted the code from @chqrlie since it more close to the OP code but added the locale, otherwise it fails to produce correct output for extended/Unicode characters.

#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;
}
  1. You can use getwc() only on unoriented or wide-oriented stream. From getwc() man page : The stream shall not have an orientation yet, or be wide-oriented.

  2. It is not possible to change stream orientation, if the stream already has orientation. From fwide() man page : Calling this function on a stream that already has an orientation cannot change it.

  3. Stream opened with glibc's fmemopen() has an byte-orientation and therefore can't be wide-oriented in any way. As described here uClibc has fmemopen() routine without this limitation.

Conclusion: You need to use uClibc or another library or make your own fmemopen() .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM