Обновить
69
Владимир@Googolplex

Software engineer

25
Подписчики
Отправить сообщение
и не всегда возможно (или нужно)

Хоть это не всегда возможно, это нужно безусловно. Если вы не знаете кодировку входных данных, вы должны либо работать с ней как с массивом байт, либо, если по каким-то причинам вы не можете зафиксировать кодировку в API, но при этом должны обрабатывать входные данные как текст (например, искать в тексте какие-то слова), то нужно пытаться интерпретировать входную последовательность байт в разных кодировках. Например, если вам нужно найти какое-то слово в тексте, а его кодировка совершенно неизвестна, то вам нужно преобразовать своё слово в десяток наиболее подходящих кодировок и искать его байтовое представление в байтах, составляющих входной текст.
Это если бы я был студентом, а вы профессором

Прошу прощения, но это абсолютно необоснованное утверждение. Именно из-за того, что программисты путают юникод и его кодировки, возникают опасные представления о том, как с ним правильно работать. Unicode — это таблица символов, ничего больше. Представление code point'ов юникода в виде последовательность байт (т.е. кодировка) — это один из Unicode Transformation Format'ов, т.е. UTF'ов. Пытаться называть их как-то ещё, даже если их так называют в любимом языке — это путь к неправильному пониманию того, как работает юникод, и к ошибкам в программе.

Вот цитата из документации Tcl:
Strings in Tcl are logically a sequence of 16-bit Unicode characters. These strings are represented in memory as a sequence of bytes that may be in one of several encodings: modified UTF-8 (which uses 1 to 3 bytes per character), 16-bit “Unicode” (which uses 2 bytes per character, with an endianness that is dependent on the host architecture), and binary (which uses a single byte per character but only handles a restricted range of characters). Tcl does not guarantee to always use the same encoding for the same string.

Из этого следует, что внутреннее представление строк в тикле ущербно — как и в Java, например. Если оставить в стороне некорректное использование терминологии Unicode, получается, что естественной кодировкой для строк в тикле является UCS-2 (потому что именно UCS-2 позиционировалась, как fixed-byte-кодировка с 16-битными «символами»). Два остальных представления (modified UTF-8 и binary, которая, похоже, ASCII) строго совместимы с UCS-2. В частности, из-за в тикле не получится естественным образом представить code point'ы юникода извне BMP — такие строки, скорее всего, будут изображать UTF-16 состоять из двух «символов» (как в Java).

Ваш первый пример — наполовину читерский, наполовину удачливый. Там нет никакого binary в UTF-8 представлении. Там имеется следующее.

Во-первых, строки в тикле действительно могут менять внутреннее представление и содержать «символы» переменной длины. Здесь важно то, что convertfrom обрабатывает только первый байт каждого «символа». Более того, строки в тикле используются для хранения как символьных данных, так и бинарных (что, с учётом их внутренного представления, по меньшей мере недальновидно).

Во-вторых, переменные у вас имеют следующий смысл:

txt — строка во внутреннем представлении, полученная декодированием последовательности байт в кодировке cp1252

utfAsBin — последовательность байт в кодировке UTF-8, полученная кодированием txt
utfAsBin2 — строка во внутреннем представлении, полученная декодированием последовательности байт в кодировке UTF-8

utfAsBin2 — строка во внутреннем представлении, полученная декодированием txt как последовательности байт в кодировке cp1251 (из-за того, что convertfrom рассматривает только нижние байты каждого символа, а в UCS-2 символы с кодами 128-255 совпадают с Latin-1, с которой cp1252 наполовину бинарно совместима, всё проходит замечательно — символ ö распознаётся как ц и декодируется в ц)
utfAsBin — последовательность байт в кодировке UTF-8, полученная кодированием utfAsBin2

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

В-третьих, читерство заключается в том, как вы формируете $map — сначала там берутся буквы с умляутами во внутреннем представлении, затем берётся их нижний регистр, а затем каждый из них преобразуется в байтовую строку в кодировке UTF-8, и всё это склеивается обратно в список. Именно поэтому эта функция работает на первых двух строках одинаково — в первой группе utfAsBin она находит в одном случае символ в UTF-8, а в другом — во внутреннем представлении. Убираем из кода строчку
set map [concat $map [encoding convertto utf-8 $map]]

и закономерно получаем:

Бинарный массив содержащий utf-8 'Schröder' (как-бы двойная кодировка) ...
Ищем Ä Ö Ü ä ö ü в Schröder (53636872c3b6646572) ...
No! probably not a german text, does not contains german umlauts, found char 'ö' (c3b6)

Или это правда символы cp1252 'Schröder' но напрямую utf-8 ...
Ищем Ä Ö Ü ä ö ü в Schröder (53636872f6646572) ...
Yes! may be german text, contains german umlaut 'ö' (f6)

Бинарный массив содержащий utf-8 'Schrцder' (как-бы двойная кодировка, тот же символ, но из cp1251, не должен найти) ...
Ищем Ä Ö Ü ä ö ü в Schrцder (53636872d186646572) ...
No! probably not a german text, does not contains german umlauts, found char 'ц' (d186)

Utf-8 (тот же символ, но из cp1251) 'Schrцder' напрямую utf-8 ... (не должен найти) ...
Ищем Ä Ö Ü ä ö ü в Schrцder (5363687246646572) ...
No! probably not a german text, does not contains german umlauts, found char 'ц' (46)


Ваш код сломается на китайских символах с вероятностью, близкой к 100%. Про какие-нибудь эмодзи я уже и не говорю.

Ваш второй пример всего лишь демонстрирует преобразование массива байт в кодировках cp1251/cp1252 во внутреннее представление (UCS-2-совместимое) и затем обратно в массив байт в кодировке UTF-8 или UCS-2. Не очень понятно, что здесь имелось в виду.
но важно, чтобы она предполагала фиксированное количество байт на любой символ

Это невозможно, если под символом понимать графемный кластер. Графемный кластер может состоять из произвольного количества code point'ов. Понимать же под символом code point — очень опасное дело, особенно если вам нужно переставлять «символы».
Да, как мне кажется, движки регулярных выражений — это одно из исключений в плане выбора кодировки. Если предполагается обрабатывать юникодные строки, то такие движки должны работать в UTF-32.

Что такое обработка binary в UTF-8-представлении? о_О

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

Насчёт нужности автоматического преобразования между внутренними представлениями строки не уверен, но это не так важно — главное, чтобы всегда было известно, где какая кодировка. В Rust, например, вы всегда можете преобразовать String в Vec<u8> (получить сырые байты строки, обозначающие символы в UTF-8) и в Vec<char> (получить вектор 32-битных code unit'ов, непосредственно соответствующих code point'ам), причём первое преобразование «бесплатно», второе же требует аллокации и преобразования кодировки.

Кстати, что вы подразумеваете под unicode-представлением? Unicode — это просто таблица символов, это не кодировка. Тот же вопрос про binary.
Вот, кстати, очень хороший пример, когда посимвольная замена не работает в любом случае, вне зависимости от кодировки строк (цитата с utf8everywhere):

toupper() and tolower() shall not be phrased in terms of code units, as it does not work in Unicode. For example, the Latin ffl ligature must be converted to FFL and the German ß to SS (there is a capital form ẞ, but the casing rules follow the traditional ones).
банального char * хватит за глаза и за уши

Строго говоря, именно об этом говорится на utf8everywhere — следует использовать char *-строки как хранилище UTF-8, и конвертировать в другие кодировки на границах с другими API (что в основном актуально только для винды, потому что в Unix-системах системные вызовы работают с байтами, и им наплевать на кодировки).
Подписываюсь под каждым словом!
проблемы с заменой символов

Определите сначала, что такое «символ». Есть как минимум 2 варианта — code point и grapheme cluster. Если вы пишете, скажем, текстовый редактор, то вам нужно работать именно с кластерами, и там в любом случае не обойдётся заменой одного байта на другой, вне зависимости от кодировки строк. Работать с code point'ами же в плане замены очень опасно — например, очень легко сломать диакритику.

Кроме того, настоящая кодировка с фиксированной длиной code unit'а — это UTF-32. UTF-16 с двух- и четырёх-байтовыми code unit'ами имеет совершенно те же проблемы, что и UTF-8, в плане замены code point'ов. Если нужно часто осуществлять такие операции, то строку в любом случае нужно сконвертировать в UTF-32. Но в качестве основного формата хранения строк UTF-32 не пригоден, потому что у него огромный оверхед по памяти.
Вот это следует прочитать всем: utf8everywhere.org. Отлично расписано, почему нет никаких причин использовать что-то, кроме UTF-8.

Насчёт питона, кстати, тоже не всё так однозначно: lucumr.pocoo.org/2014/1/5/unicode-in-2-and-3, lucumr.pocoo.org/2014/5/12/everything-about-unicode. Я считаю, что разделение строк на юникодные и байтовые абсолютно необходимо, но, судя по всему, в Python 3 это сделано, по меньшей мере, странно.

На мой взгляд, удобнее всего строки реализованы в Go и Rust (особенно в последнем). В них строка — это просто набор байт, обозначающий текст в UTF-8. Всё. Эти языки так же обеспечивают гарантии того, что все строки являются валидными UTF-8-последовательностями.
Это Pebble Time. Здесь проблема в том, что конкретно мой случай — это пара Time + Time Steel, они рассылаются в разное время (Time будет в конце мая, Time Steel в июле). Не возникнет ли здесь сложностей с подтверждением платежа? Кроме того, я прочитал, что в таможенной декларации нужно указывать много всего (вроде адреса веб-магазина), но эти часы пока что официально не продаются, и на getpebble.com пока что просто информационная странца про них.
Могут ли возникнуть проблемы с доставкой через вас, если я хочу переслать reward, полученный в кампании на Kickstarter?
Опередили вы меня :) я как раз собирался её переводить.

Посмотрите существующие переводы на хабре, чтобы разобраться в переводе терминологии: habrahabr.ru/post/256941, habrahabr.ru/post/256211, habrahabr.ru/post/254961, habrahabr.ru/post/243315, habrahabr.ru/post/237199.
Смотрят значит в одну сторону — светло, звёзды там, галактики и т.п., смотрят в другую, а там нет нифига…

На самом деле по современным представлениям вселенная однородна и изотропна, и в любой её точке вы будете видеть всё вокруг примерно одинаково. Нет такой штуки, как «край» вселенной, за которым пустота и ничего нет.
Да, насчёт скалы такое ощущение иногда создаётся :) много там всего намешано, многое тянется как груз в виде совместимости с джавой. Те же XML-литералы — когда скала только создавалсь, все думали что XML — это язык разметки будущего, но не сложилось, и теперь их собираются депрекейтить.

В цейлоне очень крутая система дженериков и type refinement'а, и очень классные фичи вроде union-типов, которых в скале, бывает, очень не хватает. Очень здоровская система модулей. Но, я боюсь, он не получит достаточного распространения, как, например, и Kotlin, потому что ниша не-Java JVM-языков уже занята Scala/Groovy, и новые языки не дают каких-то особых преимуществ перед ними (а иногда даже наоборот, например, в том же котлине для нормального взаимодействия с Java-библиотеками нужно писать гору аннотаций — хотя мб там за год что-то уже поменялось).
Да, Rust действительно строится вокруг концепции владения данными и заимствования (те самые ownership и borrowing). Например, предыдущая статья показывает, как здорово эти фичи помогают работать с потоками. Остальные фичи (вроде трейтов, паттерн-матчинга и енумов), конечно, тоже важны, но они не настолько «центральны» и не являются чем-то уникальным в Rust.

Но, что интересно, Rust не всегда был таким :) столь серьёзный упор на владение и заимствование начался примерно год-полтора назад, когда стало понятно, что эти концепции очень мощные и позволяют реализовать кучу абстракций почти забесплатно. До этого Rust был гораздо больше похож на Go — рантайм, гринтреды, встроенные в язык каналы. Так что к текущему дизайну пришли в результате экспериментов и серьёзных правок в «мировоззрении».
ZeroMQ — это реализация AMQP? o_O

Информация

В рейтинге
Не участвует
Откуда
Santa Clara, California, США
Дата рождения
Зарегистрирован
Активность