UTF-8

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

UTF-8

Непрочитанное сообщение Olej » 02 ноя 2022, 15:33

Известно, что UTF-8 - это способ кодирования символов в представлении Unicode.
Что в старых языках создаёт много проблем для локализованных не ASCII строк...

Например, в C (стардарты POSIX.1-2001, POSIX.1-2008, C99) строки в UTF-8 (multibyte characters) обрабатываются группой функций mb*() (mblen() и др.). А Unicode символы представляются типом wchar_t, и обюрабатываются функциями (аналогами str*() в ANSI C) вида wc*() (wcslen() и др.) + множеством функций осуществляющих взаимодействие и преобразования wchar_t и multibyte characters (типа mbsrtowcs(), mbrtowc() и др.) см.: wchar.h - wide-character handling.

В Linux (GCC) Unicode символы, wchar_t вседа имеют размер 4 байта, представление UTF-32 ... независимо что они там чудят в Windows (и в cygwin), с их представлением Unicode как UTF-16, см.: wchar_t string on Linux, OS X and Windows.

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

Я об этих вещах ... по крайней мере, относительно "классики" - C/C++ - написал, в своё время, 2 статьи: Локализация и регулярные выражения, C/C++
... которые, в конечном итоге, слились в один общий текст...

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: UTF-8

Непрочитанное сообщение Olej » 02 ноя 2022, 16:38

Olej писал(а):
02 ноя 2022, 15:33
в старых языках создаёт много проблем
Но вот в новых языках авторы сразу пошли на все символьные представления в UTF-8:
- Go: Go + Примеры кода Go + cборка приложений Go + GUI на Go Go : инструментарий (продолжение)
- Rust: Rust + код на Rust + Rust: новый подход к снаряду...

P.S. Да и Python в линейке версий 3.Х установлена дефаултная кодировка UTF-8
Теперь можно в заголовке кода Python не выписывать явно, что требовалось в версиях Python 2.X:

Код: Выделить всё

# -*- coding: utf-8 -*-

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: UTF-8

Непрочитанное сообщение Olej » 02 ноя 2022, 16:44

Olej писал(а):
02 ноя 2022, 15:33
Это всё достаточно общеизвестные вещи
Но мне очень понравилось объяснение от авторов Rust - Байты, скалярные значения и кластеры графем! Боже мой!...
Настолько понравилось, что полностью скопирую это сюда:
Ещё один момент, касающийся UTF-8, заключается в том, что на самом деле существует три способа рассмотрения строк с точки зрения Rust: как байты, как скалярные значения и как кластеры графем (самая близкая вещь к тому, что мы назвали бы буквами).

Если посмотреть на слово языка хинди «नमस्ते», написанное в транскрипции Devanagari, то оно хранится как вектор значений u8 который выглядит следующим образом:

[224, 164, 168, 224, 164, 174, 224, 164, 184, 224, 165, 141, 224, 164, 164,
224, 165, 135]

Эти 18 байт являются именно тем, как компьютеры в конечном итоге сохранят в памяти эту строку. Если мы посмотрим на 18 байт как на скалярные Unicode значения, которые являются Rust типом char, то байты будут выглядеть так:

['न', 'म', 'स', '्', 'त', 'े']

Здесь есть шесть значений типа char, но четвёртый и шестой являются не буквами: они диакритики, специальные обозначения которые не имеют смысла сами по себе. Наконец, если мы посмотрим на байты как на кластеры графем, то получим то, что человек назвал бы словом на хинди состоящем из четырёх букв:

["न", "म", "स्", "ते"]

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

Последняя причина, по которой Rust не позволяет нам индексировать String для получения символов является то, что программисты ожидают, что операции индексирования всегда имеют постоянное время (O(1)) выполнения. Но невозможно гарантировать такую производительность для String, потому что Rust понадобилось бы пройтись по содержимому от начала до индекса, чтобы определить, сколько было действительных символов.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: UTF-8

Непрочитанное сообщение Olej » 02 ноя 2022, 20:40

Olej писал(а):
02 ноя 2022, 16:38
в новых языках авторы сразу пошли на все символьные представления в UTF-8
Меня в этой связи заинтересовал вопрос проверить: насколько глубоко проникает UTF-8 в синтаксис языка?
Т.е. ... можно ли использовать локализованные символы Unicode только в содержимом текстовых литералов (строк) ... или, например, и в записи идентификаторов в коде?

В этой связи начал с Go ... про который априорно знаю что "можно" :lol:

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$ cat utf8go.go 
package main
import "fmt"

func main() {
        строка := "строка"
	fmt.Printf("вывод: %s\n", строка)

        안녕하세요 := "안녕하세요"
	fmt.Printf("вывод: %s\n", 안녕하세요)
}
Второе слово - это слово языка хинди ... которое я не знаю что означает, и которое я тупо списал из документации Rust. :lol:

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$ go build -o utf8go utf8go.go

olej@R420:~/2022/Rust/utf-8$ ls -l utf8go 
-rwxrwxr-x 1 olej olej 1796565 ноя  2 18:53 utf8go

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$ ./utf8go
вывод: строка
вывод: 안녕하세요
Вложения
utf8go.go
(223 байт) 46 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: UTF-8

Непрочитанное сообщение Olej » 02 ноя 2022, 20:43

Olej писал(а):
02 ноя 2022, 20:40
начал с Go
Дальше - Python ... где я, в значительной мере, предполагаю что будет:

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$ cat utf8py.py 
строка = "строка"
print("вывод: {}".format(строка))

안녕하세요 = "안녕하세요"
print("вывод: {}".format(안녕하세요))

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$ python3 utf8py.py
вывод: строка
вывод: 안녕하세요
Как и предсказывал! ;-)
А вот Python 2:

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$ python2 utf8py.py
  File "utf8py.py", line 9
SyntaxError: Non-ASCII character '\xd1' in file utf8py.py on line 10, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

olej@R420:~/2022/Rust/utf-8$ python utf8py.py
  File "utf8py.py", line 9
SyntaxError: Non-ASCII character '\xd1' in file utf8py.py on line 10, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details
... который, как видите, до сих пор дефаултный в Linux.
Вложения
utf8py.py
(161 байт) 45 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: UTF-8

Непрочитанное сообщение Olej » 02 ноя 2022, 20:51

Olej писал(а):
02 ноя 2022, 20:43
Дальше - Python
Ну и, наконец, Rust с которого всё началось:

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$ cat utf8rs.rs 
fn main() {
    let строка = "строка";
    println!("вывод: {}", &строка);

    let 안녕하세요 = "안녕하세요";
    println!("вывод: {}", &안녕하세요);
}
Компиляция (без всяких там проектов cargo):

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$ rustc utf8rs.rs
warning: the usage of Script Group `Cyrillic` in this crate consists solely of mixed script confusables
  --> utf8rs.rs:19:9
   |
19 |     let строка = "строка";
   |         ^^^^^^
   |
   = note: `#[warn(mixed_script_confusables)]` on by default
   = note: the usage includes 'а' (U+0430), 'к' (U+043A), 'о' (U+043E), 'р' (U+0440), 'с' (U+0441), 'т' (U+0442)
   = note: please recheck to make sure their usages are indeed what you want

warning: 1 warning emitted
Во как! :
1). это warning, но не error ...
2). исполнимый файл создан:

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$
ls -l utf8rs
-rwxrwxr-x 1 olej olej 3888344 ноя  2 19:30 utf8rs
3). для `Cyrillic` строки предупреждение возникает ... а для хинди - нет :-o
4). почти наверняка это предупреждение можно удалить ... какими-нибудь прагмами, определениями ... но я пока не разбирался как.
Выполнение:

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$ ./utf8rs
вывод: строка
вывод: 안녕하세요
Идентично предыдущим. ;-)
Вложения
utf8rs.rs
(196 байт) 47 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: UTF-8

Непрочитанное сообщение Olej » 02 ноя 2022, 21:26

Olej писал(а):
02 ноя 2022, 20:51
4). почти наверняка это предупреждение можно удалить ... какими-нибудь прагмами, определениями ... но я пока не разбирался как.
Лёгкий поиск приводит к обстоятельному описанию как: Allow non-ASCII letters (such as accented characters, Cyrillic, Greek, Kanji, etc.) in Rust identifiers.
Start Date: 2018-06-03
RFC PR: rust-lang/rfcs#2457
As an implementation note, it may be worth dealing with confusable modifiers via a separate lint check -- if a modifier is from a different (non-Common/Inherited) script group from the thing preceding it. This has some behavioral differences but should not increase the chance of false positives.

The exception for Latin is made because the standard library is Latin-script. It could potentially be removed since a code base using the standard library (or any Latin-using library) is likely to be using enough of it that there will be non-confusable characters in use. (This is in unresolved questions)
Но если поместить непосредственно в файл .rs : #[allow(confusable_idents)] ... то получите новое предупреждение:

Код: Выделить всё

warning: allow(confusable_idents) is ignored unless specified at crate level
 --> main.rs:2:9
  |
2 | #[allow(confusable_idents)]
  |         ^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_attributes)]` on by default
Разъяснение как сделать это на уровне крейта Rust здесь: Is ignored unless specified at crate level
И, в конечном, итоге:

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$ cat utf8rs.rs 
#![allow(mixed_script_confusables)]

fn main() {
    let строка = "строка";
    println!("вывод: {}", &строка);

    let 안녕하세요 = "안녕하세요";
    println!("вывод: {}", &안녕하세요);
}

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$ rustc utf8rs.rs

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$ ls -l utf8rs 
-rwxrwxr-x 1 olej olej 3888344 ноя  2 20:07 utf8rs

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$ ./utf8rs 
вывод: строка
вывод: 안녕하세요
Вот и всё. :-D
Вложения
utf8rs.rs
(233 байт) 44 скачивания

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: UTF-8

Непрочитанное сообщение Olej » 02 ноя 2022, 23:37

Olej писал(а):
02 ноя 2022, 21:26
Вот и всё.
И напоследок, пока, иллюстрация того как Rust работает с самыми разнообразными языками (которые никак соотносятся с locale которые установлены в системе!):

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$ cat mullang.rs 
fn greet_world() {
    let regions = [
        String::from("السلام عليكم"),
        String::from("Dobrý den"),
        String::from("Hello"),
        String::from("שָׁלוֹם"),
        String::from("नमस्ते"),
        String::from("こんにちは"),
        String::from("안녕하세요"),
        String::from("你好"),
        String::from("Olá"),
        String::from("Здравствуйте"),
        String::from("Hola"),
    ];

    for region in regions.iter() {
            println!("{}", &region);
    }
}

fn main() {
    greet_world();
}
Если у кого возникнет подозрение (зависть? :lol: ) что я знаю так много разных языков... :-o - то нет, я просто бесстыдно списал вид символьных литералов откуда-то из документации Rust :oops: ... но вот код как ними распорядиться я написал сам:

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$ rustc mullang.rs 
olej@R420:~/2022/Rust/utf-8$ ./mullang 
السلام عليكم
Dobrý den
Hello
שָׁלוֹם
नमस्ते
こんにちは
안녕하세요
你好
Olá
Здравствуйте
Hola
Компиляция:

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$ rustc mullang.rs 
Выполнение:

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$ ./mullang 
السلام عليكم
Dobrý den
Hello
שָׁלוֹם
नमस्ते
こんにちは
안녕하세요
你好
Olá
Здравствуйте
Hola
Вложения
mullang.rs
(592 байт) 49 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: UTF-8

Непрочитанное сообщение Olej » 03 ноя 2022, 01:56

Ну и окончательно, о том как представляется Rust строка в символах и в байтах ...
Байты, скалярные значения и кластеры графем! Боже мой!
Ещё один момент, касающийся UTF-8, заключается в том, что на самом деле существует три способа рассмотрения строк с точки зрения Rust: как байты, как скалярные значения и как кластеры графем (самая близкая вещь к тому, что мы назвали бы буквами).

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$ cat loopch.rs 
fn main() {
    let string = "Привет".to_string();

    for c in string.chars() {
        println!("{}", c);
    }

    for b in string.bytes() {
        println!("{}", b);
    }
}

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$ rustc loopch.rs 

Код: Выделить всё

olej@R420:~/2022/Rust/utf-8$ ./loopch 
П
р
и
в
е
т
208
159
209
128
208
184
208
178
208
181
209
130
Длина строки в символах и байтах, что и понятно, различаются.
Вложения
loopch.rs
(187 байт) 46 скачиваний

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 23 гостя