Первое, на что я натолкнулся, начав изучать Эрланг - это проблемы со строками, отягощенные всей юникодностью нашего алфавита. Пожалуй, будь моим родным языком английский (или латынь или любой другой с алфавитом, полностью помещающимся в первые 128 символов utf-8), часть проблем бы я просто не заметил, и даже считал, что так оно и должно быть и я все делаю правильно. К счастью, мне сразу пришлось разбираться со всеми особенностями.
Первая и самая главная вещь - в Эрланге нет строк. То есть вообще нет, как класса, как отдельной единицы смысла. Отсюда вывод - unicode-строк здесь тоже нет. Поначалу это кажется странным и не логичным (во многом из-за того, что непривычно и идет ломка шаблона).
Строкой здесь считается список int`ов, которую можно напечатать на экране. Поэтому когда вы пишете
> Str = "ABCDEF".
то на самом деле вы создаете никакую не строку, а а список чисел [65,66,67,68,69,70]. В этом легко убедиться:
> [65,66,67,68,69,70] =:= Str.
true
Но и это тоже еще не фатально, во многих языках строка часто мимикрирует под массив, что позволяет удобно работать с ней как с массивом. И здесь вы так же получаете похожие возможности, например, так можно взять 3-ий символ строки:
> Char = lists:nth(3, Str).
67
Я думаю вас уже не удивило появление здесь именно 67, а не "С"? Потому что раз нет строк, то и символы тоже не нужны. Символ - это просто его код в таблице.
Если хотите увидеть как он выглядит на экране, напечатайте его как символ:
> io:format("~c~n", [Char]).
C
ok
Вторая важная мысль. Как еще можно представить строку текста? Конечно, как последовательность байт. Что, кстати говоря, выглядит даже логичнее. В любом случае, когда вы эту строку будете куда-то отправлять или записывать в файл, запишется именно последовательность байт.
Для бинарных данных есть специальный объект - binary.
Здесь все просто - последовательность байт она и в Африке последовательность байт:
> SB = <<"ABCDEF">>.
<<"ABCDEF">>
Можно убедиться, что это последовательность тех же самых байт, что и в списке:
> <<"ABCDEF">> =:= <<65,66,67,68,69,70>>.
true
С латиницей разобрались, казалось бы что нового может дать unicode?
Но вот тут-то и начинается веселье. Беда в том, что в список символов записывается не его представление в utf-8, а его номер в таблице unicode - тот самый code point.
Именно поэтому строка "АБВГД" так сильно отличается от своего латинского аналога:
> "АБВГД".
[1040,1041,1042,1043,1044]
А вот двоичное представление этой строки в utf-8 совсем другое:
> RB = unicode:characters_to_binary("АБВГД").
<<208,144,208,145,208,146,208,147,208,148>>
И если мы этот бинарь побайтово напечатаем, то получим не тоже самое, что в случае первого списка:
> io:fwrite("~w~n", [erlang:binary_to_list(RB)]).
[208,144,208,145,208,146,208,147,208,148]
ok
Что с этим делать?
Выбрать правильный формат и применять по месту. При необходимости одно можно конвертировать в другое. Для этого вам поможет ряд полезных функций:
1) RB = unicode:characters_to_binary("АБВГД"). % из списка юникодных символов делает бинарь в utf-8
<<208,144,208,145,208,146,208,147,208,148>>
2) unicode:characters_to_list( <<208,144,208,145,208,146,208,147,208,148>> ). % из бинаря в utf-8 делает список
[1040,1041,1042,1043,1044]
Вы же еще помните, что так выглядит строка "АБВГД"?
3) unicode:characters_to_list("АБВГД"). % причем вы можете "скормить" и список, только смысла в этом немного
[1040,1041,1042,1043,1044]