Размер файла 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";