LINUX.ORG.RU

Как читать буфер памяти посимвольно в заданной кодировке?

 , ,


0

1

Здрасьти.

Хочу что-то такое:

uint8_t* buf = { open(argv[1]); бинарный read(); close(); buf[filesize] = 0; }
чтото_типа_set_charset_for_getc(argv[2]);
while ((int c = чтото_типа_getc(buf)) != 0) {
    блаблабла мистер Фримен;
}

Можно конечно тупо сконвертнуть весь буфер в wstring и потом работать с ним как белый человек. Но мой внутренний байтодрочер ужасается от sizeof(wchar_t) и подозревает, что посимвольно читать буфер будет быстрее.


SOLVED:

(1) тыц, тыц.

(2) Для парсинга сорцов это всё не нужно, по крайней мере поначалу.

★★★★★

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

Никак. Ибо тот же ютф8 имеет переменную длину кода символа. wchar_t для произвольной кодировки тоже никак есть коды большие чем wchar_t.

чтото_типа_getc(buf) ну да, а внутри парсинг кодировки, извлечение кода символа и возврат, и дай бог символ сам по себе, а не из нескольких закорючек и нескольких кодов, только не произвольной, её указывать надо, они разные. И даже одинаковые разные.

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Ответ на: комментарий от LINUX-ORG-RU

Ибо тот же ютф8 имеет переменную длину кода символа.

Это понятно, но ведь конвертеры строк из кодировки в кодировку по-любому работают посимвольно, т.к. иначе никак. Вот мне мечтается найти такое публичное API – хоть C linux-only (что-нибудь из glibc/iconv/…), хоть C++. Т.е. чтобы всё как в потрохах этих конвертеров, но вместо дописывания очередного сконвертированного символа в target buffer оно отдавало бы его мне.

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

Аааа, ну один хрен надо кодировку то указывать. А то их как бы много. Не, ну есть определялки, но они как бы точно не вс могут определить.

iconv
wchar_t
int c = чтото_типа_getc(buf)

Чёт тип такого навреное, под себя переделать и норм наверное. Но кодировку всё равно надо указывать.

http://www.gnu.org/software/libc/manual/html_node/iconv-Examples.html

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

Аааа, ну один хрен надо кодировку то указывать.

Ну так я указываю во второй строчке:

чтото_типа_set_charset_for_getc(argv[2]);

Сгодился бы и контекст, передаваемый в каждый вызов getc(), как по твоей ссылке (cd = iconv_open ("WCHAR_T", charset);); но дальше оно опять-таки по площадям шарашит, а не посимвольно (nconv = iconv (cd, &inptr, &insize, &wrptr, &avail);). Но видимо надо поковырять этот iconv, может найдётся что-нибудь типа iconv_getc(buf, cd).

dimgel ★★★★★
() автор топика

Но мой внутренний байтодрочер

Ну тебе же этот массив кодов символов нужен будет разок и всё. И да вот например buf[filesize] ну будет раскодированный массив в 4 раза больше вместо 1 метра 4 метра. Обработал, память освободил. Зато такты экономишь. Один раз всё пееркодировал в массив, его обработал освободил память и ляпота. Разве что тебе гигабайты текста нужно обрабатывать, тогда да. Можно экономить память, но в замен ты будешь жрать больше процессора. Идеального выхода нету, разве что ты изначально все входящие данные приведёшь к одной нужной кодировке и избежишь бесполезного кода в таком случае для всего и вся. Ну я так, мысли в слух я не программист.

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

Обработал, память освободил. Зато такты экономишь. […] Разве что тебе гигабайты текста нужно обрабатывать, тогда да.

Читать буду исходники произвольного проекта, многопоточно. Так что – от 100 байт до гигабайтов.

Кроме того, мой личный опыт подсказывает, что даже при довольно скромных объёмах, память – ГОРАААЗДО более тормозная штука, чем один и тот же закешированный код работающей с ней функции. Хотя конечно, зависит от того, что там за функция getc(); но это и замерить можно. Да и даже если она тяжёлая – она будет выполнена в любом случае, независимо от того, куда пойдёт её результат: ко мне или в буфер. Так что её вызов можно сократить с обоих сторон уравнения.

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

Прикольно, сенькс. Если разные сорцы в разных кодировках, можно и сгруппировать; хотя пока что я такое поддерживать не собираюсь.

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

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

В общем случае не посимвольно. Потому что из нескольких символов одной кодировки может быть один символ другой. И даже один и тот же символ может превращаться в разные в зависимости от того, какие символы рядом.

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

Что там на входе творится – мне вообще пофиг; вот если какая-нибудь входная последовательность даст два или более взаимосвязанных выходных символа, это будет веселее. Но надеюсь, какую-нибудь заковыристую букву внутри комментария я никогда не перепутаю с '\n' или "*/", этот комментарий завершающими. Т.е. никакая буква не развернётся в группу wchar_t, содержащую '\n' или '*', которые я могу спутать с конструкцией языка.

dimgel ★★★★★
() автор топика
Последнее исправление: dimgel (всего исправлений: 4)
Ответ на: комментарий от xaizek

Есть mbrtowc().

Хотя… Если на каждый вызов оно лезет в getenv("LC_CTYPE"), парсит его, инициализирует что там нужно в потрохах на тему кодировки… Не, это будет кромешная дичь. Контекст необходим.

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

Если ты ищешь конструкции из ASCII символов, то никакой модификатор не будет иметь код из ASCII набора.

И ещё тут, по-моему, непонятки с терминологией: один wchar_t - это code point, один или более code point - grapheme cluster. mbrtowc даёт первое, для деления на второе - можно заюзать utf8proc::utf8proc_grapheme_break(), но по-моему тебе не нужно.

ICU - говно, плюсовые codecvt - сырое, в сях тоже швах, вокруг боль и страдания.

Крестовый вариант чтения:

#include <fstream>
#include <locale>
#include <iostream>
using namespace std;

int main()
{
    auto loc = locale("en_US.UTF8");
    wfstream f("ff");
    f.imbue(loc);

    wchar_t val;
    while (f.get(val), f) {
        cout << val << endl;
    }
}
kvpfs ★★
()
Ответ на: комментарий от kvpfs

Если ты ищешь конструкции из ASCII символов, то никакой модификатор не будет иметь код из ASCII набора.

Ага, гуд. Что-то такое смутно помнил, но слишком смутно.

один wchar_t - это code point, один или более code point - grapheme cluster

Мама, а можно я не пойду сегодня в школу?

Крестовый вариант чтения:

Спасибо за компактный пример (мне в гугле везде этот громоздкий codecvt попадается), но ifstream – сильно не та вещь, которую я стал бы использовать ради скорости. Где я выше упоминал «хоть C++», допускался и какой-нибудь самопал, например с контекстом инкапсулированным в класс.

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

Хм. Но если написано, что зависит от переменной окружения, значит в лучшем случае оно должно кешироваться и сбрасывать кеш при вызовах setenv(). Что-то как-то слишком сложно, хотя и не невозможно. Если в iconv ничего не найду, попробую и сравню скорости с конвертацией всего файла.

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

но ifstream

А есть какие-то замеры? Оно действительно сильно тормозит? Я никогда не заморачивался потестить.

ради скорости

я не знаю что там, но на всякий случай - для поиска ascii вообще не нужно конвертировать narrow кодировку в wchar_t.

допускался и какой-нибудь самопал, например с контекстом инкапсулированным в класс

есть сишное, можно велосипедить. Я костылил вокруг libiconv (но мне нужно было более, чем один лишь wchar_t).

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

Я про переменные не говорил.

Ну я это из man mbrtowc вычитал. Хотя вычитал неправильно. Как говорится, «но есть ньюанс»: «The behavior of mbrtowc() depends on the LC_CTYPE category of the current locale.» Так что да, всё должно быть чики-пуки.

dimgel ★★★★★
() автор топика
Ответ на: комментарий от kvpfs

А есть какие-то замеры? Оно действительно сильно тормозит? Я никогда не заморачивался потестить.

Вот эта моя приблуда умеет генерировать гигантский отладочный вывод. Когда я заменил в ней ostream на printf, даже с > /dev/null она ускорилась на порядки.

dimgel ★★★★★
() автор топика
Ответ на: комментарий от kvpfs

я не знаю что там, но на всякий случай - для поиска ascii вообще не нужно конвертировать narrow кодировку в wchar_t.

Значит ли это, что в мультибайтовых символах все байты (кроме может быть первого) >127?

dimgel ★★★★★
() автор топика
Ответ на: комментарий от xaizek

Не было. :) Про endl я знаю. Я ускорению, помнится, и сам удивился – т.к. по моим понятиям, там разница разве что в виртуальных вызовах (хотя и форматтеры крайне неудобные и уродские). С третьей стороны, у меня там не просто printf, а sprintf в буфер с последующим одним-единственным системным вызовом write(), который atomic и не требует доп.синхронизаций (а через cout я построчно выводил ЕМНИП с использованием спинлока), может ещё и в этом был фокус: сорц.

А может и endl был; иначе какой смысл в спинлоке если оно буфер не сбросит в конце оператора. Да, наверное был. Но это собственно не недостаток моего кода, а неизбежность если не хочешь чтобы вывод от разных потоков перемешался.

А то, если вставить fflush() после fprintf() скорость тоже может просесть.

А вот с atomic write() не проседает. :-P Но и с обычным printf() ускорение тоже было ЕМНИП заметное, и fflush() там тоже наверняка был, иначе никак. А то что я нахимичил – это уже байтодрочерство чисто прикола ради: посмотреть что получится.

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

Значит ли это, что в мультибайтовых символах все байты (кроме может быть первого) >127?

ASCII символы будут <128, все остальные - больше.

Вот эта моя приблуда умеет генерировать гигантский отладочный вывод. Когда я заменил в ней ostream на printf, даже с > /dev/null она ускорилась на порядки.

Погуглил, первый попавшийся тест говорит, что разницы там нет.

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

Разница есть и это общеизвестно. Хотя и не такая огромная, которой я у себя добился как описано выше.

Причём не только из-за виртуальных вызовов, а ещё например и потому, что printf("%s %s\n", "Hello", "World") – это один вызов, а cout << "Hello" << " " << "World" << "\n" – четыре. А если использовать форматирование, то и того больше.

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

Причём не только из-за виртуальных вызовов, а ещё например и потому, что printf(«%s %s\n», «Hello», «World») – это один вызов, а cout << «Hello» << " " << «World» << «\n» – четыре

Ну как бы сишному printf’у тут надо format string распарсить …

В общем ладно, если сишное io такое быстрое, то почему работает медленнее крестового?

#include <stdio.h>
 
int main()
{
	const char *s1 = "dkjfdkjfkdfjkd";
	const char *s2 = "dkjfdkjfkdfjkd";
	const char *s3 = "dkjfdkjfkdfjkd";

	FILE* fp = fopen("/tmp/rr", "w");
	for (int i = 0;  i < 10000000;  ++ i)
		fprintf(fp, "%s %s %s", s1, s2, s3);
}


#include <fstream>
using namespace std;
 
int main()
{
	const char *s1 = "dkjfdkjfkdfjkd";
	const char *s2 = "dkjfdkjfkdfjkd";
	const char *s3 = "dkjfdkjfkdfjkd";

	ofstream f("/tmp/rr2");
	for (int i = 0;  i < 10000000;  ++ i)
		f << s1 << ' ' << s2 << ' ' << s3;
}

$ gcc 1.c -O2 -o ce
$ g++ 2.cpp -O2 -o cppe
$ time ./ce
real    0m4.184s

$ time ./cppe
real    0m2.848s

$ cmp /tmp/rr /tmp/rr2
kvpfs ★★
()

Честно говоря так и не понял, чем тебя iconv не устроил. Читаешь в буфер сколько-нибудь символов, конвертируешь во что-нибудь, с чем тебе удобно работать, в тот же UTF-8, скажем, и отрабатываешь сконвертированный кусок. Потом следующий и так далее. Хочешь по одному кодопоинту обрабатывать — конвертируй в UTF-32 и в выходном буфере под 4 байта место выделяй, хотя это и не слишком производительно будет, но на фоне I/O вероятно любой код будет достаточно быстрым. Но лучше — UTF-8 и буфер килобайтов на 16.

vbr ★★★★
()
Последнее исправление: vbr (всего исправлений: 3)
Ответ на: комментарий от LINUX-ORG-RU

там еще коррекция ошибок есть из-за коллизий, те чтобы коныертировать строку в ютф, ее нужно всегда полностью читать, но в высокоуровневых языках можно читать посимвольно…

>>> open('test.txt', 'w').write('писька')
6
>>> open('test.txt').read(1)
'п'

но тут мне, кажется, что какая-то магия и содержимое файла для этого фокуса полностью в оперативку считывается всегда

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

если utf-16 позволяет большой символ разбить на 2 слова, то ucs2 его просто не сможет закодировать и потеряет

Факт (см. example). Но вопрос был про… хм, вопрос был идиотский: ясен хрен что двухбайтную кодировку по байту парсить смысла вообще не имеет.

В общем, кину монетку или либо заюзаю mbrtowc() сразу, либо для начала забью.

dimgel ★★★★★
() автор топика
Ответ на: комментарий от mittorn

Не понял. Локаль я буду временно менять программно перед парсингом файлов. С этой точки зрения не вижу разницы с логикой декодирования всего файла с помощью того же iconv().

dimgel ★★★★★
() автор топика