LINUX.ORG.RU

Unicode C/C++ programming


0

0

Может кто-нибудь подсказать документацию, в которой было бы описано что нужно делать (на примерах), чтобы программа работала с Unicode?

Насколько я понял, для Unicode был создан тип данных wchar_t, однако, насколько я опять же понял, этот тип данных жёстко двухбайтовый - что, вроде как не очень подходит для программ.

Есть и другой вопрос - почему-то ни в одной библиотеке, которая в Unicode работает, я не увидел wchar_t - бывает или int (??) или обычный char (например, снизу функция из iconv/extra/) - объясните, пожалуйста - что к чему...

Заранее благодарен.

------------------------------
/*
* This C function converts an entire string from one encoding to another,
* using iconv. Easier to use than iconv() itself, and supports autodetect
* encodings on input.
*
* int iconv_string (const char* tocode, const char* fromcode,
* const char* start, const char* end,
* char** resultp, size_t* lengthp)
*
* Converts a memory region given in encoding FROMCODE to a new memory
* region in encoding TOCODE. FROMCODE and TOCODE are as for iconv_open(3),
* except that FROMCODE may be one of the values
* "autodetect_utf8" supports ISO-8859-1 and UTF-8
* "autodetect_jp" supports EUC-JP, ISO-2022-JP-2 and SHIFT_JIS
* "autodetect_kr" supports EUC-KR and ISO-2022-KR
* The input is in the memory region between start (inclusive) and end
* (exclusive). If resultp is not NULL, the output string is stored in
* *resultp; malloc/realloc is used to allocate the result.
*
* This function does not treat zero characters specially.
*
* Return value: 0 if successful, otherwise -1 and errno set. Particular
* errno values: EILSEQ and ENOMEM.
*
* Example:
* const char* s = ...;
* char* result = NULL;
* if (iconv_string("UCS-4-INTERNAL", "autodetect_utf8",
* s, s+strlen(s)+1, &result, NULL) < 0)
* perror("iconv_string");
*
*/
------------------------------


> Может кто-нибудь подсказать документацию, в которой было бы описано что нужно делать (на примерах), чтобы программа работала с Unicode?

Ничего. Если локаль UTF-8, то работаешь как обычно, только надо помнить, что 1 символ != 1 байт. Это что касается C. Для C++ обычно юникод тащат с собой фреймворки - в GLib/Gtk это опять же UTF-8 (но уже локаленезависимый) и свои функции для работы с ним, в Qt - UTF-16 и QString, и так далее. wchar_t и std::wstring - это _не обязательно_ юникод в том смысле, что стандартная библиотека не делает никаких предположений касательно того, что ты туда запихиваешь (кроме стандартных настроек локали). Больше того, размер wchar_t строго не определен, насколько я помню, он >=short. Под виндой это как правило 2 байта, в линухе вроде бы все 4. По этой причине библиотеки для работы с юникодом его обычно не используют.

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

> Для C++ обычно юникод тащат с собой фреймворки - в GLib/Gtk это опять же UTF-8

1) С каких это пор gtk стал фреймворком для С++? он всю жизнь был на C написан и на него же ориентирован.

2) На самом деле, в STL юникод довольно хорошо поддерживается, по крайней мере, в теории. Но иногда реализация страдает кривизной

aa5779
()

wchar_t отнюдь не жестко двухбайтовый. Напротив, в *nixах он обычно 4-байтовый. Но на самом деле, покажите мне человека который бы использовал Unicode-символы с кодами больше 0xFFFF. Поэтому я не очень понимаю, почему wchar_t "не подходит для программ".

Стандартные С функции для работы с wchar_t, естественно, его используют (напр., man wcscpy)

iconv не использует в явном виде wchar_t в силу своей общности. Но по крайней мере в glibc, iconv в качестве charset'а принимает "WCHAR_T"

Если использовать только UTF-8, то тогда можно использовать char * и соответствующие функции. К сожалению, в стандартной С библиотеке нет функции типа strnext, которая бы выдавала бы указатель на следующий символ (не байт), хотя написать ее, посмотрев на описание utf-8, особого труда не составляет.

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

> С каких это пор gtk стал фреймворком для С++? он всю жизнь был на C написан и на него же ориентирован.

Он будет фреймворком для С++ до тех пор, пока С++ не перестанет содержать С (точнее часть С, но gtk вполне в эту часть укладывается) как подмножество.

> На самом деле, в STL юникод довольно хорошо поддерживается, по крайней мере, в теории

Теоретиков - на костёр.

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

Мне нужна работа в Unix/Linux, весьма желательно в рамках стандартов ANSI & POSIX, но насколько я понимаю, все (подавляющее большинство) приложений работает с char...

Локаль у меня может быть какая угодно - это не должно мешать программе, но я не могу понять следующего: как мне написать то, что называют Unicode-ная программа, особенно, если есть библиотеки, которые работают как обычно... В то же время, есть вроде как продвинутые (пример - в первом посте) и они вроде как работают с этим юникодом, но тогда в упор не понимаю - почему они работают с типом данных char...

В общем в голове какая-то каша, и я никак не могу это всё структурировать... =/

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

#uname -sr
Darwin 7.6.0

#info libc
No menu item `libc' in node `(dir)Top'.

========================
#uname -sr
FreeBSD 5.2-RELEASE

#info libc
No menu item `libc' in node `(dir)Top'.

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

То есть можно использовать wchar_t для хранения Unicode (проблема только в функциях, корректно с ним работающими)?

Извиняюсь, не очень понял фразу "Если использовать только UTF-8, то тогда можно использовать char * и соответствующие функции" - то есть тип char может хранить Unicode (без потерь)?

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

1) Тип wchar_t НУЖНО использовать "для храннения Unicode", если, конечно, у вас нет каких-то специальных резонов не делать этого.

2) Стандартные C функции для работы с широкими строками, по крайней мере в glibc, работают так же корректно, как и стандартные функции с обычными строками.

3) Тип char разумеется не может хранить Unicode (потому что во всех реализациях С, о которых я знаю, CHAR_BITS < 16).

Но UTF-8 --- это способ записывать Unicode строки как 8-битные строки. В том смысле, что строка, содержащая данные в кодировке utf-8, будет иметь тип char * и работать с ней нужно будет с помощью обычных, а не "широких" функций

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

То есть, если мне будет достаточно UTF-8, то можно использовать тип char. Если нужно все UTF-16, то использовать нужно только wchar_t и все соответсвующие ему функции. Я правильно понял?

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

Блин, узнай что такое utf-8 и не задавай больше дурацких вопросов.

man 7 utf8

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

utf-16 и utf-8 покрывают один и тот же диапазон codepoint'ов из Unicode. Но utf-16 --- это 16-битная кодировка, а utf-8 -- это multibyte encoding.

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

> Но utf-16 --- это 16-битная кодировка, а utf-8 -- это multibyte encoding.

Тогда уж utf-16 - multiword encoding.

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

> С каких это пор gtk стал фреймворком для С++? он всю жизнь был на C написан и на него же ориентирован.

Извиняюсь, оговорился. Но смысл тот же.

> На самом деле, в STL юникод довольно хорошо поддерживается, по крайней мере, в теории. Но иногда реализация страдает кривизной

Эээ... где? Покажи пример поддержки уникода там?

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

> Локаль у меня может быть какая угодно - это не должно мешать программе, но я не могу понять следующего: как мне написать то, что называют Unicode-ная программа, особенно, если есть библиотеки, которые работают как обычно... В то же время, есть вроде как продвинутые (пример - в первом посте) и они вроде как работают с этим юникодом, но тогда в упор не понимаю - почему они работают с типом данных char...

Короче говоря, иди-ка ты разбираться с GLib. Жить проще будет...

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

> wchar_t отнюдь не жестко двухбайтовый. Напротив, в *nixах он обычно 4-байтовый. Но на самом деле, покажите мне человека который бы использовал Unicode-символы с кодами больше 0xFFFF.

Опять синдром "640Кб должно хватить всем"?

> Поэтому я не очень понимаю, почему wchar_t "не подходит для программ".

Потому что для оптимизации строковых функций часто полезно знать точный размер в байтах одного символа. А с wchar_t это весьма муторно определить. Проще пихать все в short или long.

int19h ★★★★
()

Походил (везде куда послали), поразбирался (с чем смог), и, в общем, нашёл несколько действительно полезных ссылок (может кому, кроме меня будет полезно):

- http://en.wikipedia.org/wiki/UTF-8
- http://www.cl.cam.ac.uk/~mgk25/unicode.html
- http://www.gotroot.it/index.php?mod=howto&name=Unicode-HOWTO&wwwgr_Se...
- ftp://ftp.ilog.fr/pub/Users/haible/utf8/Unicode-HOWTO.html


И, собственно, самое главное (простое и чёткое объяснение):
=======================================

1.2. Unicode encodings

<...>

There are basically four ways to encode Unicode characters in bytes:

UTF-8
128 characters are encoded using 1 byte (the ASCII characters).
1920 characters are encoded using 2 bytes (Roman, Greek,
Cyrillic, Coptic, Armenian, Hebrew, Arabic characters). 63488
characters are encoded using 3 bytes (Chinese and Japanese among
others). The other 2147418112 characters (not assigned yet) can
be encoded using 4, 5 or 6 characters. For more info about
UTF-8, do `man 7 utf-8' (manpage contained in the man-pages-1.20
package).

UCS-2
Every character is represented as two bytes. This encoding can
only represent the first 65536 Unicode characters.

UTF-16
This is an extension of UCS-2 which can represent 1112064
Unicode characters. The first 65536 Unicode characters are
represented as two bytes, the other ones as four bytes.

UCS-4
Every character is represented as four bytes.

The space requirements for encoding a text, compared to encodings
currently in use (8 bit per character for European languages, more for
Chinese/Japanese/Korean), is as follows. This has an influence on disk
storage space and network download speed (when no form of compression
is used).


UTF-8
No change for US ASCII, just a few percent more for ISO-8859-1,
50% more for Chinese/Japanese/Korean, 100% more for Greek and
Cyrillic.

UCS-2 and UTF-16
No change for Chinese/Japanese/Korean. 100% more for US ASCII
and ISO-8859-1, Greek and Cyrillic.

UCS-4
100% more for Chinese/Japanese/Korean. 300% more for US ASCII
and ISO-8859-1, Greek and Cyrillic.

Given the penalty for US and European documents caused by UCS-2,
UTF-16, and UCS-4, it seems unlikely that these encodings have a
potential for wide-scale use. The Microsoft Win32 API supports the
UCS-2 encoding since 1995 (at least), yet this encoding has not been
widely adopted for documents - SJIS remains prevalent in Japan.

UTF-8 on the other hand has the potential for wide-scale use, since it
doesn't penalize US and European users, and since many text processing
programs don't need to be changed for UTF-8 support.

In the following, we will describe how to change your Linux system so
it uses UTF-8 as text encoding.


1.2.1. Footnotes for C/C++ developers

The Microsoft Win32 approach makes it easy for developers to produce
Unicode versions of their programs: You "#define UNICODE" at the top
of your program and then change many occurrences of `char' to `TCHAR',
until your program compiles without warnings. The problem with it is
that you end up with two versions of your program: one which
understands UCS-2 text but no 8-bit encodings, and one which
understands only old 8-bit encodings.

Moreover, there is an endianness issue with UCS-2 and UCS-4. The IANA
character set registry http://www.isi.edu/in-
notes/iana/assignments/character-sets says about ISO-10646-UCS-2:
"this needs to specify network byte order: the standard does not
specify". Network byte order is big endian. And RFC 2152 is even
clearer: "ISO/IEC 10646-1:1993(E) specifies that when characters the
UCS-2 form are serialized as octets, that the most significant octet
appear first." Whereas Microsoft, in its C/C++ development tools,
recommends to use machine-dependent endianness (i.e. little endian on
ix86 processors) and either a byte-order mark at the beginning of the
document, or some statistical heuristics(!).

The UTF-8 approach on the other hand keeps `char*' as the standard C
string type. As a result, your program will handle US ASCII text,
independently of any environment variables, and will handle both
ISO-8859-1 and UTF-8 encoded text provided the LANG environment
variable is set accordingly.

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

Да, и спасибо всем за помощь и ответы!

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

Для wchar_t размер в байтах одного символа будет, грубо говоря, sizeof(wchar_t) который может быть либо 2 (напр., для win32) либо 4 (напр., для linux/glibc). wchar_t это НЕ мультбайтная кодировка. Иными словами, wchar_t --- это просто синоним для short, long или какого-нибудь еще целочисленного типа (ну то есть в C++ это формально отдельный тип, но сути это не меняет). Дело только в том, что все стандартные функции С которые имеют отношение к данной теме требуют именно wchar_t -- и использование любого другого типа с ними будет работать до тех пор, пока кто-нибудь не рещит, что ~2^31 символов тоже не всем может хватить и не сделать реализацию c sizeof(wchar_t) == 1024 ;)

Что касается STL, то все классы, имеющие отношение к символам, имеют два варианта (ну то есть это инстанциации шаблона basic_whatever) -- один для обычных символов (тип char), другой для широкий (тип wchar_t). Это касается string/wstring, это касается *stream/w*stream. Кроме того, в STL есть поддержка локалей и через их посредство -- перекодировки Unicode в 8-битовые кодировки.

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

> Для wchar_t размер в байтах одного символа будет, грубо говоря, sizeof(wchar_t) который может быть либо 2 (напр., для win32) либо 4 (напр., для linux/glibc). wchar_t это НЕ мультбайтная кодировка.

А что, UCS-16, где _по 2 байта на символ_ - это по-твоему не мультибайтная кодировка??

> Дело только в том, что все стандартные функции С которые имеют отношение к данной теме требуют именно wchar_t -- и использование любого другого типа с ними будет работать до тех пор, пока кто-нибудь не рещит, что ~2^31 символов тоже не всем может хватить и не сделать реализацию c sizeof(wchar_t) == 1024 ;)

Однако же нигде в описании стандартных функций не оговаривается, что они оперируют над юникодными строками. О чем и речь.

> Кроме того, в STL есть поддержка локалей и через их посредство -- перекодировки Unicode в 8-битовые кодировки.

Вот именно что через локали. Есть в _системе_ юникодная локаль - будет вам юникод, и то только если она выставлена. Нет - обломитесь. Ни язык, ни стандартная библиотека ничего такого не гарантируют.

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

> А что, UCS-16, где _по 2 байта на символ_ - это по-твоему не мультибайтная кодировка??

В известном смысле --- да, но суть-то в том, что на один символ всегда приходится по 2 байта. В отличие от UTF-8.

> Вот именно что через локали. Есть в _системе_ юникодная локаль - будет вам юникод, и то только если она выставлена. Нет - обломитесь. Ни язык, ни стандартная библиотека ничего такого не гарантируют.

Ничего подобного. Строго говоря, действительно wchar_t и все что с ним связано не обязан иметь отношение к Unicode (т.е. если бы было что-то альтернативное). Более того, никем не гарантируется *степень* поддержки Unicode, потому что это штука хитрая. Однако что из себя представляет wchar_t определяется реализацией, а не текущей локалью и вообще не настройкой системы. Конечно, функция, допустим, iswupper может возращать неожиданное значение для символа, которого нет в текущей LC_CTYPE -- т.е. при текущей локали en_US.iso8859-1 символ U+0430 может не восприниматься (и скорее всего не будет) как буква. Но в локали ru_RU.koi8-r --- будет.

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

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

> Конечно, функция, допустим, iswupper может возращать неожиданное значение для символа, которого нет в текущей LC_CTYPE -- т.е. при текущей локали en_US.iso8859-1 символ U+0430 может не восприниматься (и скорее всего не будет) как буква. Но в локали ru_RU.koi8-r --- будет.

А как это по-твоему называется, если не поддержка юникода через локаль? Если корректность работы функций зависит от выставленной локали?

int19h ★★★★
()

Я использовал для работы с юникодными строками std::wstring. В UNIX'ах никаких проблем, в win32 есть небольшие проблемы с кодировкой консоли, но они легко решаются. В UNIX'ах wchat_t обычно четырехбайтовый, кодировка UCS-4. iconv для работы с юникодом вообще не обязателен, не обращай на него внимания.

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

Теорию понял, возникли некоторые проблемы с практикой, так что снова здравствуйте... =)

Написал простенькую программу, которая берёт utf-8 файл и посредством тупого копирования переписываёт его содержимое в другой файл (копируя по одному символу).

Проблема в том, что почему-то, неизменно, в конец файла добавляемся некоторый символ '\377', на который приходится русская буква "я" (все остальные символы - в порядке).

Подскажите, пожалуйста, в чём проблема.

Листинг:
/* Header files */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main (void) {

FILE *testfile_in, *testfile_out;
unsigned char ch1 = NULL, ch2 = NULL;

printf("sizeof ch1 - %li bytes\n", sizeof ch1);
printf("sizeof ch2 - %li bytes\n", sizeof ch2);

testfile_in = fopen("test_file.in", "r");
testfile_out = fopen("test_file.out", "w");

if (testfile_in == NULL) {
printf("Input file open error.\n");
return 0;
}/* if */

if (testfile_out == NULL) {
printf("Output file open/creation error.\n");
return 0;
}/* if */

while (!feof(testfile_in)) {
ch1 = getc(testfile_in);
ch2 = ch1; //make copy
putc(ch2, testfile_out); //write down copy
}/* while */

fclose(testfile_in);
fclose(testfile_out);

return 0;
}/* main */

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

Извиняюсь, проблему нашёл - правильный вариант - ниже:

while (ch1 = getc(testfile_in), !feof(testfile_in)) {
ch2 = ch1; //make copy
putc(ch2, testfile_out); //write down copy
}/* while */

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