habrahabr

Почему QR-коды в верхнем регистре меньше, чем в нижнем?

  • пятница, 28 февраля 2025 г. в 00:00:15
https://habr.com/ru/articles/885990/

Взгляните на эти два QR-кода. Отсканируйте их, если хотите: обещаю, в них нет ничего опасного.

Слева HTTPS://EDENT.TEL/ в верхнем регистре, а справа — https://edent.tel/ в нижнем.

Можно чётко заметить, что слева QR-код «меньше», то есть в нём меньше битов данных. Оба ведут на один и тот же URl, единственное различие заключается в регистре.

Что здесь происходит?

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

Левый имеет «Type 1» и размер 21px * 21px. Правый — «Type 2» и размер 25px * 25px.

В официальной спецификации версии описаны подробнее. В меньший код должно умещаться 25 алфавитно-цифровых символов. Но https://edent.tel/ имеет длину всего 18 символов. Почему же эта строка превратилась в больший код?

При помощи декодера наподобие ZXING можно просмотреть сырые байты каждого кода.

UPPER

20 93 1a a6 54 63 dd 28  35 1b 50 e9 3b dc 00 ec11 ec 11

lower

41 26 87 47 47 07 33 a2  f2 f6 56 46 56 e7 42 e746 56 c2 f0 ec 11 ec 11  ec 11 ec 11 ec 11 ec 11 ec 11

Можно заметить, что оба они заканчиваются одинаковой последовательностью ec 11. Это «байты-заполнители», необходимые, потому что данные должны полностью заполнять QR-код. Но постойте, почему QR-код верхнего регистра не только вполне может уместить в себе текст, но и имеет лишний заполнитель?

Ответ таится в первой паре байтов.

После считывания сырых байтов сканер QR-кодов должен точно знать, с каким типом кода он имеет дело. Первые четыре бита сообщают ему режим. Давайте преобразуем шестнадцатеричные данные в двоичные и выделим первые четыре бита:

Тип

HEX

BIN

Разбиение

UPPER

20 93

00100000 10010011

0010 000010010011

lower

41 26

01000001 00100110

0100 000100100110

Для UPPER используется код 0010; это обозначает, что он алфавитно-цифровой, и стандарт гласит, что следующие 9 битов показывают длину данных.

Для lower используется код 0100, который означает байтовый режим; стандарт гласит, что длину данных показывают следующие 8 битов.

Тип

HEX

BIN

Разбиение

UPPER

20 93

00100000 10010011

0010 0000 10010

lower

41 26

01000001 00100110

0100 000 10010

Посмотрите на это! Оба они имеют длину 10010; если преобразовать значение из двоичной системы, получится 18, то есть длина текста.

Алфавитно-цифровой режим использует по 11 битов на каждые два символа, а байтовый режим использует (как можно догадаться) по 8 битов на один символ.

Почему же QR-код в нижнем регистре генерируется в байтовом режиме? Разве в нём не используются буквы и цифры?

Вообще да, но для эффективного хранения данных у алфавитно-цифрового режима есть только ограниченное подмножество символов. Это буквы в верхнем регистре и несколько пунктуационных символов: пробел $ % * + - . / :

К счастью, этого хватает для хранения протокола, домена и пути. Но, увы, не параметров GET.

Так что имейте в виду: если вам нужен минимальный физический размер QR-кода, содержащий URl, то весь текст должен быть написан заглавными буквами.