Размер файла Fstream в кодовых точках

Есть много вопросов о получении размера файла в файле std::fstream, но все они возвращают размер файла в байтах и ​​подвержены ошибкам, если файл открыт в другом потоке.

Я хочу знать размер файла в кодовых точках, а не в байтах.

Сейчас std::fstream::seekg(0,std::ios::end) с последующим std::fstream::tellg() возвращает только длину в байтах. Это не говорит мне, сколько символов UTF-16/32 находится в файле. Разделите результат на sizeof(wchar_t) Я слышу, как ты говоришь. Не работает для файлов UTF-8 и НЕ является переносимым.

Теперь, для более технических единомышленников, у меня есть imbued поток с моим собственным std::codecvt учебный класс. std::codecvt имеет члена length() который, учитывая два указателя в потоке, вычисляет длину и возвращает максимальное или количество выходных символов. Я бы подумал, что поиск по файлу будет искать по codecvt::intern_type а не по основанию char тип.

Я посмотрел в fstream заголовок и обнаружил, что искать Infact не использует codecvt, И, на моей версии от VS2010, codecvt::length() член даже не упоминается. Infact, при каждом звонке codecvt::in(), новый строковый объект создается и увеличивается в размере на 1 символ каждый раз in() возвращается partial, Вместо этого он не вызывает codecvt::max_length() участник и снабдить вызов адекватным буфером.

Это только моя реализация или я могу ожидать, что другие сделают то же самое? имеет std::fstream был переписан для VS2012, чтобы в полной мере использовать локали?

По сути, мне надоело писать собственные обработчики файлов каждый раз, когда я использую текстовые файлы. Я надеюсь создать fstream производный класс, который будет сначала читать файлы спецификации, если они есть, и наполнять codecvt, Затем преобразовать эти символы в char, wchar_t или как того требует код. Я также надеюсь закодировать его таким образом, чтобы, если известно предварительное знание кодировки, locale можно указать на строительстве.

Буду ли я лучше работать напрямую с внутренним буфером, перезаписывать аффект класса fstream или есть какие-то хитрости, о которых я не знаю?

2 ответа

Решение

Если я вас правильно понимаю, вы ожидаете, что:

`std::basic_fstream<CharT,Traits>::seekg`

(который по наследству basic_istream<CharT,Traits>::seekg), следует выполнить операцию позиционирования потока в единицах, которые являютсяintern_type из чего угодно codecvt которым пропитан поток.

шаблон basic_istream объявлено:

template< 
    class CharT, 
    class Traits = std::char_traits<CharT>
> class basic_istream;

В объявлении функции-члена:

basic_istream & basic_istream<CharT,Traits>::seekg(pos_type pos)

pos_type является std::char_traits<CharT>::pos_type который поэтому является типом, определяемым в любой реализации исключительно CharT шаблонный аргумент basic_istream класс и без ссылки на любой codecvt,

basic_fstream<char>например, остается basic_fstream<char>, И его pos_type остатки basic_fstream<char>::pos_typeнезависимо от кодировки, выбранной для чтения или записи.

Приведенные выше объявления соответственно соответствуют стандарту C++11 § 27.7.1 и § 27.7.2.1. Дело в том, что pos_type инвариантен относительно любого проникшего codecvtи, следовательно, также поведение seekg(pos_type), следовательно, являются последствиями стандарта.

Эквивалентные замечания применяются для basic_istream& seekg( off_type off, std::ios_base::seekdir dir),

std::codecvt::intern_type является типом элементов внутренней последовательности, в которую или из которой указанное кодирование будет переводить внешнюю последовательность элементов типа extern_type,intern_type тип элемента последовательности "в программе" и extern_type тип последовательности "в файле" intern_typeне имеет ничего общего с операциями позиционирования файла.

Если вы должны выяснить размер файла в кодовых точках и предположить, что возможными интересующими кодировками являются UTF-8, UTF-16 и UTF-32, то для первых двух из них у вас нет выбора, кроме как прочитать все файл, поскольку они являются кодировками переменной длины, с кодовой точкой UTF-8, занимающей 1-4 байта, и кодовой точкой UTF-16, занимающей 2 или 4 байта. UTF-32 представляет собой 4-байтовую кодировку фиксированной длины, поэтому в этом случае вы можете вычислить количество полных кодовых точек как длину в байтах файла, минус длина BOM, если она есть, деленная на 4, если вы не учитываете возможность ошибок кодирования, кроме конца файла.

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

length функция std::char_traits возвращает количество CharTсимволы, которые не обязательно являются количеством байтов. Итак, в основном вам нужно прочитать буфер вашего файла в std::string и распечатать его size():

std::ofstream out("out.txt");
out.rdbuf()->pubimbue(std::locale("en_US.UTF8"));

std::streambuf* p = out.rdbuf();
p->pubseekoff(0, std::ios_base::beg);

std::string data; //  use std::u16string for UTF-16 data

data.assign(std::istreambuf_iterator<char>(out),
            std::istreambuf_iterator<char>());

std::cout << "We have " << data.size() << " codepoints";
Другие вопросы по тегам