LINUX.ORG.RU

unicode, read file, С

 , ,


1

2

не могу осилить работу с юникодом, wchar и не могу найти внятную инфу по этой теме с примерами. например есть файл в котором есть слово «Москва», размер файла 13 байт. как я понимаю размер каждой буквы 2 байта, конец файла 1 байт? sizeof(wchar) = 4 байта. если я пишу что-то типа:

setlocale(LC_ALL, "");
FILE *fd;
fd = fopen("test", "r");
fseek(fd,0,SEEK_END);
int size = ftell(fd);
fseek(fd,0,SEEK_SET);
wchar buff[size];
fgetws(buff,size,fd); //Здесь всегда выдает NULL
for (int i=0; buff[i] != '\0'; i++) { //не могу настроить чтобы в терминале вывод работал с wchar, поэтому такие костыли
    putchar(wctob(buff[i]));
}
putchar('\n');
помогите, пожалуйста, разобраться что я делаю не так.



Последнее исправление: SmilePlz (всего исправлений: 2)

Ответ на: комментарий от SmilePlz

Вот так вот работает:

#include <err.h>
#include <locale.h>
#include <stdio.h>
#include <wchar.h>

int
main()
{
	FILE *fd;
	wchar_t buff[1024];

	setlocale(LC_ALL, "");

	fd = fopen("test", "r");
	if (!fd)
		errx(1, "cannot open test file");

	while (fgetws(buff, sizeof(buff), fd))
		fputws(buff, stdout);

	fclose(fd);

	return 0;
}

И ещё 2 маленьких замечания:

  • тип размера — size_t, а не int
  • тип в-чара — wchar_t, а не wchar
beastie ★★★★★
()

В файле utf8, скорее всего. А тринадцатый байт, бьюсь об заклад, \n.

mix_mix ★★★★★
()

Интересно. Похоже, последний fseek(), который устанавливает указатель на начало, срабатывает как-то не так. Если вместо этого fseek закрыть файл и открыть заново, то все читается нормально.

А если еще вместо этой конструкции с putchar печатать printf-ом, то и выводится нормально

Deleted
()
Ответ на: комментарий от Deleted

Возможно. В BSD есть, кстати, ещё ftello, который возвращает off_t.

В любом случае, простого int недостаточно.

beastie ★★★★★
()
Ответ на: комментарий от Deleted

Похоже, последний fseek(), который устанавливает указатель на начало, срабатывает как-то не так

Непонятно, баг это или фича, но такая комбинация fseek-ов (сначала на конец, потом в начало) действительно отрабатывает странно. После этого fgetws и fgetwc ведут себя как будто указатель остался в конце.

Код из ОП начинает работать, если добавить еще один fseek перед позиционированием на начало:

        fseek(fd, 1, SEEK_END); /* вот с этим работает */
        fseek(fd, 0, SEEK_SET);

Но вообще лучше получать размер файла другим способом. fstat например.

Да, это с более-менее свежим линуксовым libc, у других наверное по-другому

Deleted
()
Ответ на: комментарий от Deleted

Хм, похоже что и в самом деле баг. Проверил на OpenBSD — там всё работает, как и положено.

beastie ★★★★★
()
Ответ на: комментарий от Deleted

Буфферизированное чтение

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

void
error(char *msg)
{
        fprintf(stderr, "%s: %s\n", msg, strerror(errno));
        exit(EXIT_FAILURE);
}

int main()
{
        FILE *fd;
        long size = 0;
        wchar_t buff[7]; /* в слове Москва 6 букв + 1 для \0 */

        setlocale(LC_CTYPE, "en_US.UTF-8"); /* NOTES, man fgetws */

        fd = fopen("test", "r");
        if (fseek(fd, 0L, SEEK_END) == -1)
                error("fseek [1]");
        size = ftell(fd);
        if (fseek(fd, 0L, SEEK_SET) == -1)
                error("fseek [2]");
        fflush(fd);
        if (fgetws(buff, 7, fd) == NULL) /* <-- 7 это кол-во символов + \0 */
                error("fgetws");
        wprintf(L"size = %i: %ls\n", size, buff);
        return 0;
}

Все работает в gcc-5.3.0+musl-1.1.12 (даже без fflush()), gcc-4.7.2 + eglibc-2.13

man fflush

       For input streams associated with seekable files (e.g., disk files, but
       not pipes or terminals), fflush() discards any buffered data that has
       been fetched from the underlying file, but has not been consumed by the
       application.

gh0stwizard ★★★★★
()
Последнее исправление: gh0stwizard (всего исправлений: 3)
Ответ на: комментарий от Kiborg

А fgets и fgetc работают нормально?

Да, странно ведут себя только wide-функции

gh0stwizard

С fflush() работает как ожидается. Ну, мне кажется что это все-таки баг, и на месте ТС таким способом измерять размер файла не стал бы

Deleted
()
Ответ на: комментарий от Deleted

Да, странно ведут себя только wide-функции

Скорее наоборот, т.к. в мане кроме fflush ни строчки о том как именно работают fget***-функции. Толи буферизированно, толи нет. Кое-где даже упоминиается, что можно их смешивать с fseek/ftell и т.д. Т.о. они уже полагаются на поведение fflush (буфферизацию). А вот wide-функции, наоборот, не полагаются. Видимо wide-функции не используют position из структуры FILE (если он там прописан, конечно).

gh0stwizard ★★★★★
()
Ответ на: комментарий от Deleted

Похоже, последний fseek(), который устанавливает указатель на начало, срабатывает как-то не так.

Я с такой проблемой сталкивался на uClibc 0.9.33.2.

Relan ★★★★★
()
Ответ на: комментарий от beastie

В соседней теме выяснили, что в разных средах у wchar_t может быть разный вес в байтах, а потому лучше использовать char16_t и char32_t, которые по стандарту гарантируют свою битность. И подправить достаточно всего ничего:

#include <err.h>
#include <locale.h>
#include <stdio.h>
#include <wchar.h>
#include <uchar.h>

int
main()
{
        FILE *fd;
        char32_t buff[1024];

        setlocale(LC_ALL, "");

        fd = fopen("test", "r");
        if (!fd)
                errx(1, "cannot open test file");

        while (fgetws(buff, sizeof(buff), fd))
                fputws(buff, stdout);

        fclose(fd);

        return 0;
}

saahriktu ★★★★★
()

спасибо всем за участие, у меня просто руки кривые. вчера вечером разобрался.

SmilePlz
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.