C прочитать файл unicode

Обновлено: 06.07.2024

Я хочу читать по строкам текстовый файл Unicode (UTF-8), созданный Notepad, я не хочу отображать строку Unicode на экране, я хочу просто прочитать и сравнить строки!

Этот код читает файл ANSI по строкам и сравнивает строки

Что я хочу

Прочитайте файл test_ansi.txt по строке

если строка = "b" напечатает "YES!"

else напечатайте "NO!"

read_ansi_line_by_line.c

test_ansi.txt

Компиляция

Выход

Теперь мне нужно прочитать Unicode (UTF-8) файл, созданный Notepad, после более чем 6 месяцев я не нашел хорошего кода/библиотеки в C, который может читать файл, закодированный в UTF-8!, я не знаю именно поэтому, но я думаю, что стандарт C не поддерживает Unicode!

Чтение двоичного файла Unicode - это OK!, но проблема в том, что бинарный файл больше всего уже создан в двоичном режиме!, что означает, что если мы хотим прочитать файл Unicode (UTF-8), созданный Notepad, нам нужно перевести его из Файл UTF-8 в файл BINARY!

Этот код записывает строку Unicode в двоичный файл, ПРИМЕЧАНИЕ. C файл закодирован в UTF-8 и скомпилирован GCC

Что я хочу

Введите Unicode char "ب" в test_bin.dat

create_bin.c

Компиляция

Выход

Теперь я хочу читать двоичный файл по строкам и сравнивать!

Что я хочу

Прочитайте файл test_bin.dat по строкам если строка = "ب" напечатает "YES!" иначе напечатайте "НЕТ!"

read_bin_line_by_line.c

Выход

ПРОБЛЕМА

Этот метод ОЧЕНЬ ДОЛГО! и НЕ МОЩНО (я новичок в разработке программного обеспечения)

Пожалуйста, кто-нибудь знает, как читать Unicode файл? (я знаю, что это не просто!) Кто-нибудь знает, как конвертировать Unicode файл в двоичный файл? (простой метод) Кто-нибудь знает, как читать файл Unicode в двоичном режиме? (я не уверен)

ОТВЕТЫ

Ответ 1

Я нашел решение своей проблемы, и я хотел бы поделиться решением с любым, кто интересуется чтением файла UTF-8 на C99.

Ответ 2

Хорошим свойством UTF-8 является то, что вам нужно не декодировать, чтобы сравнить его. Заказ, возвращенный из strcmp, будет таким же, независимо от того, вы его сначала декодируете или нет. Так что просто прочитайте его как сырые байты и запустите strcmp.

Ответ 3

fgets() может декодировать кодированные файлы UTF-8, если вы используете Visual Studio 2005 и выше. Измените свой код следующим образом:

Ответ 4

В этой статье написана процедура кодирования и декодирования объясняется, как кодируется Юникод:

Его можно легко настроить на C. Просто кодируйте ANSI или декодируйте строку UTF-8 и создайте байт сравнить

EDIT: после того, как OP сказал, что слишком сложно переписать функцию из С++ здесь шаблон:

Что нужно:
+ Освободить выделенную память (или дождаться завершения процесса или проигнорировать его)
+ Добавить 4 байтовые функции
+ Скажите, что short и int не гарантируют длину 2 и 4 байта (я знаю, но C действительно глупо!) И, наконец, + Найти другие ошибки

Ответ 5

Я знаю, что я плох. но вы даже не рассматриваете BOM! Большинство примеров здесь не удастся.

Знаки байтового порядка - это несколько байтов в начале файла, которые могут использоваться для идентификации кодировки файла. Некоторые редакторы добавляют их, и много раз они просто ломают вещи по-баснословным (я помню, что в течение нескольких минут из-за этой проблемы я боролся с проблемами заголовков PHP).

Ответ 6

просто для определения аргумента спецификации. Вот файл из блокнота

с спецификацией в начале

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

В учебнике Лафоре на стр.551-553 рассказано про форматированный вывод данных в файл, получение данных из файла.

Даются две небольшие программы. Первая выводит в файл символ, целое и вещественное числа, две строки и сообщает об окончании вывода (в консоль). Вторая — читает из созданного первой программой файла данные и выводит их на экран (в консоль).



Привычно (подробности — тут) переделываю эти программы вместо работы с обычными символами (char) для работы с широкими символами (wchar_t), чтобы можно было работать в программе с Юникодом (в том числе с русскими буквами). То есть тип char меняем на wchar_t, string на wstring, ofstream на wofstream, ifstream на wifstream, cout на wcout, символьные и строковые литералы предваряем префиксом L.

Кстати, чтобы смена cout на wcout сработала, в Windows требуется переключить стандартный поток вывода в формат Юникода (в Линуксе это делается по-другому) с помощью функции _setmode, которая требует подключения заголовочных файлов <io.h> и <fcntl.h>.

Как оказалось, для вывода в файл (чтения из файла) широких символов недостаточно использования классов, работающих с широкими символами — wofstream и wifstream (я ожидал, что запись в файл будет произведена в кодировке UTF-16, исходя из того, что символы в Windows хранятся в кодировке UCS-2 (подмножество UTF-16), но этого не случилось).

Для правильного вывода русских букв в файл в данном случае требуется для текущей локали (объект соответствующего класса locale, содержащий набор параметров, определяющих региональные настройки) правильно настроить ее фасет класса codecvt (набор параметров локали поделен на отдельные разделы, называемые фасетами), регулирующий преобразование символов из одной кодировки в другую.

Раз уж итоговую кодировку текстового файла, в который будем записывать данные, всё равно нужно будет указывать, то выбираю кодировку UTF-8, как самую популярную на сегодня для текстовых файлов. Для этой кодировки имеется специальный фасет класса codecvt_utf8.

Для использования указанных классов в программу должны быть включены соответствующие заголовочные файлы <locale> и <codecvt>.

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

А после создания объекта-потока нужного класса свяжем наш поток с определенной ранее локалью, воспользовавшись методом ios::imbue:
для второй программы:

После этого обе программы должны работать правильно, а информация будет храниться в текстовом файле в кодировке UTF-8.

Программируя на С, каждый неанглоязычный программист рано или поздно сталкивается с неожиданной и досадной трудностью вывода русского текста в наборе символов Юникод (Unicode) и в кодировке UTF8. Чтение стандарта С99 и описания библиотеки libc навевает уныние, поиск в Интернете выдает массу полезных ссылок об использовании wchar_t, не приводя в то же время простых примеров работы с "wchar.h" и широкими функциями.

Эта заметка рассматривает два самых простых способа вывода русского текста из консольных С-программ.

Прежде чем мы продолжим, я хотел бы привести строки из Евангелия:


. == Первое соборное послание святого апостола Петра == . === Глава 2, Стих 24 === 24 Он грехи наши Сам вознес телом Своим на древо, дабы мы, избавившись от грехов, жили для правды: ранами Его вы исцелились.

Лично для вас благая весть - Единородный Сын Божий Иисус Христос любит вас, Он взошел на крест за ваши грехи, был распят и на третий день воскрес, сел одесную Бога и открыл нам дорогу в Царствие Небесное.

Сейчас, в это время года перед Пасхой, Православные христиане приносят Господу жертву поста и молитвы. Мы делаем это в воспоминание великого подвига Иисуса Христа, совершенного для нас и за нас. Мы вспоминаем со стыдом и раскаянием свои грехи, исповедуем их в молитве перед Богом и просим Его милости к нам, христианам. Каждый из нас молится за своих родных, друзей, соседей, коллег и всех людей, окружающих нас. Эта молитва обретает новую силу во время поста.

Прочитав эти строки, знайте - именно сейчас Православные всего мира молятся за вас, именно за вас, дорогой читатель. Мы молим Бога о вашем спасении, просим лично вас посмотреть на свою жизнь, открыть Библию, начать чтение Святого Писания и осознать как нуждается ваша душа в общении с Богом.

Покайтесь, примите Иисуса как вашего Спасителя, ибо наступают последние времена и время близко - стоит Судья у ворот.

Пожалуйста, в своих каждодневных трудах, какими бы занятыми вы себе ни казались - находите время для Бога, Его заповедей и Библии.

На главной странице этого сайта вы найдете программу для чтения Библии в командной строке - буду очень рад если программа окажется полезной. Пожалуйста, читайте Библию, на экране или в печатном виде - вы будете искренне удивлены как много там сказано лично про вас и ваши обстоятельства.

Вернемся к нашим техническим деталям.

Я предполагаю что уважаемый читатель владеет необходимыми знаниями и навыками использования С-компиляторов и знаком с языком, что позволит мне изложить самый простой подход к работе с русскими Юникод-символами в кодировке UTF8.

Простейший способ вывода

Предположим, что мы хотим всего лишь вывести некоторый текст из нашей программы, без необходимости его обработки. Сделать это можно очень просто, используя стандартные одно-байтные функции. Убедитесь что консоль системы установлена в UTF8 и текст вашей программы использует эту же кодировку.

Как видно, текст выведен правильно, несмотря на использование однобайтовых ASCII функций. Что же произошло? Фактически, мы "закодировали" нашу константу "Добро Пожаловать" не в 16 байт, а в 32 байта, и вывели их последовательно, один за другим. На экране, поток ввода-вывода stdout всего лишь преобразовал эти 32 байта в необходимые 15 двухбайтных Unicode символов (и 2 однобайтных символа), в соответствии с Unicode UTF8 encoding. Проверим это:

Заметьте выделенные жирным последовательности битов "110" и "10" в соседних байтах - это и есть кодировка UTF8. Именно так она распознается системой. Сам же Unicode код символа содержится в оставшихся битах двух соседних байтов. Также заметьте код пробела в середине и конец строки в конце - они по-прежнему однобайтные, поскольку все ASCII символы представлены в их оригинальном виде в UTF8 кодировке.

Таким образом, рассматриваемый простейший подход не является правильным решением - это всего лишь небольшая "хитрость", основанная на знании работы системы (и правильности реализации стандартной библиотеки С и эмулятора терминала).

Стандартный подход к выводу русских Unicode символов

Способ, описанный в предыдущем разделе, имеет огромный недостаток - подобный подход не позволит нам использовать поиск, преобразование символов строк и прочие функции стандартной библиотеки С. На мой взгляд, следующий подход является правильным и использует средства из стандарта ANSI C (доступные и в С99).

Строго говоря, все что мы должны были осмыслить это строку из "ISO/IEC 9899:1999", раздел "7.11.1.1 The setlocale function". Стандарт говорит: "At program startup, the equivalent of setlocale(LC_ALL, "C"); is executed". Вот она, причина вопросительных знаков или прочих "крокозяблей" на выводе!

Функции широкого вывода (например, putwchar) получают Unicode символ типа wchar_t во "внутренней" кодировке (4 байта UCS для Линукс, смотрите описание "libc"), перекодируют этот символ для "внешнего" представления в соответствии с текущей локалью ("C" по умолчанию) и выводят в поток stdout, который в нашей системе настроен на "en_EN.utf8". Вот и несоответствие - символ для однобайтной локали "С" выводится в многобайтную локаль UTF8 (и воспринимается ею как пол-символа!).

Решение очевидно - надо всего лишь сказать программе о текущей настройке локали. После этого, мы обязаны использовать только широкие версии функций и широкие типы данных wchar_t и wint_t. Поначалу это раздражает, но незначительное неудобство с лихвой компенсируется возможностью полноценной работы с широкими символами, как показано в примере - буква 'ё' правильно сменила регистр в 'Ё', заглавные буквы верно распознаются стандартными библиотечными функциями.

Также заметьте, что стандартные служебные символы (табуляции, конца строки и пр.) правильно работают в широких константах.

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

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

В третьем разделе будет рассказываться об истинно интернациональных приложениях. Здесь же мы покажем, как изменить язык интерфейса прямо во время исполнения приложения.

В последнем разделе мы опишем процесс перевода приложений на другие языки. А так же покажем, как программист и переводчик могут совместно работать над проектом, пользуясь утилитой Qt Linguist и другим инструментарием.

Текстовый движок Qt 3.2 поддерживает следующие наборы символов на всех платформах: Арабский, Китайский, Кириллический, Греческий, Иврит, Японский, Корейский, Лао, Латинский, Тайский и Вьетнамский. Кроме этого, на платформах X11 с Xft и Windows NT, дополнительно поддерживаются следующие наборы символов: Бенгальский, Девангари, Гуджарати, Каннада, Кхмерский, Сирийский, Тамильский, Телугу и Тана. На X11 поддерживаются еще Малайский и Тибетский наборы символов, а на Windows XP дополнительно поддерживается Дивехи. Если исходить из предположения, что в системе установлены соответствующие шрифты, Qt будет в состоянии отображать все символы из этих наборов.

Принципы работы с QChar, в программах, несколько отличается от принципов работы с char. Чтобы получить числовое значение символа QChar, нужно вызвать метод этого класса unicode(). Чтобы получить код символа ASCII или Latin-1, класса QChar нужно вызвать метод этого класса latin1(). Если символ не относится к поднабору Latin-1, latin1() вернет 0.

Если заранее известно, что программа будет работать исключительно с символами ASCII или Latin-1, можно использовать функции из <cctype>, такие как: isalpha(), isdigit() и isspace(). Они будут работать безотказно, потому что символы QChar автоматически преобразуются в char, в данном контексте, так же как и QString автоматически преобразуется в const char *. Однако, в любом случае лучше пользоваться функциями-членами класса QChar, для выполнения подобных операций, поскольку они будут корректно работать с любыми символами Unicode. Среди функций, которые предоставляются классом QChar, можно назвать: isPrint(), isPunct(), isSpace(), isMark(), isLetter(), isNumber(), isLetterOrNumber(), isDigit(), isSymbol(), lower() и upper(). Например, так можно проверить -- является ли символ цифрой или символом верхнего регистра:

Функция lower() возвращает версию символа в нижнем регистре. Если результат функции отличается от оригинального символа, значит это символ верхнего регистра (или заглавный символ). Этот отрывок кода справедлив для языков, которые различают регистр символов (прописные и строчные), включая латиницу, кириллицу и набор греческих символов.

Как только мы начинаем работать с Unicode-строками, у нас появляется возможность использовать их повсюду, где Qt API ожидает получить QString. В свою очередь, Qt берет на себя ответственность по корректному отображению символов строки и преобразованию в другие кодировки, если в этом возникает необходимость.

Если у вас есть желание писать в файлы любые Unicode символы, можно предложить сохранять данные как Unicode, для этого, непосредственно перед записью данных, с помощью QTextStream, нужно вызвать функцию setEncoding(QTextStream::Unicode). В результате текст будет записан в файл в кодировке UTF-16, где каждый символ представлен двумя байтами. Формат UTF-16 очень близок к представлению QString в памяти, поэтому чтение/запись строк UTF-16 выполняется очень быстро. Однако, этот формат довольно расточителен в случае символов ASCII, для хранения которых достаточно одного байта.

При чтении данных из файла, QTextStream обычно автоматически определяет Unicode, но для полной уверенности, перед выполнением процедуры чтения, лучше все-таки вызвать setEncoding(QTextStream::Unicode).

Еще одна кодировка, которая поддерживает весь набор символов Unicode -- это UTF-8. Ее основное преимущество перед UTF-16 состоит в том, что для хранения символов ASCII (символы в диапазоне 0x00..0x7F) она использует всего один байт. Все остальные символы, включая символы Latin-1, числовые значения которых лежат выше 0x7F, представлены многобайтными последовательностями. Для хранения текста, состоящего преимущественно из ASCII-символов, в формате UTF-8 потребуется практически в два раза меньше пространства, чем в UTF-16. Чтобы использовать для записи/чтения текстовых файлов формат UTF-8, предварительно нужно вызвать setEncoding(QTextStream::UnicodeUTF8).

Если предполагается использование исключительно кодировки Latin-1, вне зависимости от настроек локали пользователя, можно вызвать setEncoding(QTextStream::Latin1).

Другие виды кодировки могут быть установлены с помощью вызова функции setCodec(), передав ей соответствующий QTextCodec. Класс QTextCodec выполняет преобразование между Unicode и заданной кодировкой. Экземпляры этого класса очень широко используются в библиотеке Qt. Они используются для поддержки шрифтов, методов ввода, буфера обмена, механизма "drag-and-drop" и именования файлов.

Рассмотрим такой пример: допустим, что нам необходимо прочитать файл, записанный в кодировке EUC-KR, тогда мы могли бы написать такой код:

Некоторые форматы файлов могут содержать указание о кодировке символов в области заголовка. В данном случае, заголовок -- это некая область в начале файла, которая содержит исключительно ASCII-символы, чтобы иметь гарантированную возможность их чтения, независимо от настроек локали. Наиболее типичный пример -- файлы формата XML, которые, как правило, используют кодировку UTF-8 или UTF-16. Самый правильный способ настройки QTextStream, перед работой с XML-файлами, это вызвать setEncoding(QTextStream::UnicodeUTF8). Если файл ранее был сохранен в UTF-16, QTextStream автоматически определит это обстоятельство и скорректирует свои настройки. Заголовок XML-файла, иногда может содержать описание кодировки в заголовке <?xml?> (аргумент encoding), например: Поскольку QTextStream не допускает изменения настройки кодировки после начала процедуры чтения, то наиболее правильный подход заключается в том, чтобы начать чтение файла заново, после того как будет задана правильная кодировка (может быть получена с помощью QTextCodec::codecForName()).

Но не стоит забывать, что в случае XML, мы можем использовать специализированные классы Qt, предназначенные для работы с данными файлами (см. Главу 14), что избавит нас от необходимости беспокоиться о кодировке файлов.

Еще одна область применения QTextCodec -- задание кодировки для строк, размещаемых в исходном тексте программ. Рассмотрим такой пример: группа японских программистов разрабатывают приложение, предназначенное, в первую очередь, для внутреннего рынка. Наиболее вероятно, что исходные тексты набираются в редакторе, в кодировке EUC-JP или Shift-JIS, что позволяет им вставлять японские иероглифы прямо в текст программы, примерно так:

По-умолчанию, Qt интерпретирует аргументы функции tr() как Latin-1. Чтобы установить иную кодировку, нужно вызвать статическую функцию QTextCodec::setCodecForTr(), например: Это должно быть сделано перед самым первым вызовом функции tr(). Как правило это делается в функции main(), после создания объекта QApplication.

Но все остальные строки в программе, по прежнему будут интерпретироваться как Latin-1. Если программист хочет записать японские иероглифы в строковую переменную, он должен выполнить явное преобразование в Unicode:

Как альтернатива -- установить соответствующий кодек для выполнения преобразований между const char * и QString, вызовом QTextCodec::setCodecForCStrings(): Техника, описанная выше, может применяться к любой кодировке, не являющейся Latin-1, включая Китайскую, Греческую, Корейскую и Русскую. Ниже приводится список кодировок, поддерживаемых библиотекой Qt 3.2:

Читайте также: