Разделитель GS в кодах GS1: что это и где он теряется
GS — невидимый символ с кодом 29, который показывает, где заканчивается поле переменной длины. Без него строка кода не разбирается на поля, и касса или учётная система отклоняет код, хотя сам DataMatrix читается идеально.
Зачем нужен разделитель
В коде GS1 поля идут подряд, без пробелов: сначала две-четыре цифры идентификатора применения (AI), потом значение. У части AI длина задана стандартом — например, GTIN в поле 01 всегда 14 цифр, и парсер знает, где он кончится. У других длина переменная: серийный номер 21 может быть до 20 символов, и без разделителя не понять, где он кончается и начинается следующее поле.
Правило: после каждого поля переменной длины ставится разделитель, если за ним идёт ещё поле. После последнего поля разделитель не нужен.
[FNC1] 01 04601234567893 21 5Ab3dE7fGh9kL [GS] 91 EE10 [GS] 92 <44 символа>
└ фикс. 14 цифр ┘ └ переменная ┘ └перем.┘ └ последнее поле — без GSВ кодах «Честного знака» это даёт две типичные схемы:
| Структура | Где стоит GS |
|---|---|
| 01 + 21 + 91 + 92 (длинный код) | После серийного номера и после ключа 91 |
| 01 + 21 + 93 (короткий код) | Только после серийного номера |
| 01 + 21 + 93 + 3103 (молоко с весом) | После серийного номера и после кода проверки 93 |
Источники: состав кода для БАД и молочной продукции на сайте оператора.
FNC1 и GS — в чём разница
Внутри символа DataMatrix есть специальный кодовый символ FNC1 (кодовое слово 232). Он работает дважды:
- В начале кода FNC1 объявляет символ кодом GS1. Сканер его не передаёт, а при включённом префиксе символогии начинает строку с
]d2вместо]d1у обычного DataMatrix. - В середине кода FNC1 — разделитель полей, и сканер передаёт его как символ GS, ASCII 29 (Packaging Digest).
Некоторые программы печати кодируют в середине не FNC1, а сам символ GS. Сканер в обоих случаях выдаст одну и ту же строку, но верификатор качества может отметить это как отклонение от GS1. А вот ведущий FNC1 заменить нечем: без него код — обычный DataMatrix, и касса его не примет.
Где GS теряется
Разделитель — управляющий символ, и его тихо вырезает любое звено, которое считает его мусором:
| Звено | Что происходит | Что делать |
|---|---|---|
| Сканер в режиме эмуляции клавиатуры | GS передаётся как сочетание клавиш, которое программа игнорирует или понимает по-своему | Перевести сканер в режим виртуального COM-порта или настроить замену GS на печатный символ, который программа вернёт обратно |
| CSV, Excel, буфер обмена | Управляющие символы вырезаются при импорте и копировании | Хранить коды в файлах как получено от оператора и не прогонять через Excel |
| Шаблон этикетки | В данные попадает не символ, а текст «\x1d» или «<GS>» | Использовать способ ввода FNC1, предусмотренный вашей программой печати |
| Декодер без поддержки GS1 | Ведущий FNC1 теряется или приходит как непечатный символ | Проверять префикс ]d2 или флаг GS1 в API декодера |
| Веб-формы и JSON | Поле очищается от «невидимых» символов | Передавать как \u001d в JSON и не делать trim по управляющим символам |
Ошибаются и сами декодеры. При сверке нашего декодера с ГОСТ Р ИСО/МЭК 16022 нашлась именно такая ошибка: FNC1 дальше второй позиции терялся, а не передавался как GS. На экране такой код выглядит нормально, и заметить потерю можно только по байтам.
Как проверить, что GS на месте
Самый надёжный способ — посмотреть байты, а не текст на экране. В шестнадцатеричном виде GS — это 1d:
code = scanner.read() # строка от сканера или декодера
print(code.encode("latin-1").hex(" "))
# ... 4c 1d 39 31 ... — 1d после серийного номера, перед "91"
assert "\x1d" in code, "разделитель потерян"Если 1d нет ни в одной точке, идите по цепочке от сканера к базе данных и смотрите, на каком шаге он исчезает. Если его нет уже на выходе сканера — проблема в напечатанном коде или настройках сканера.
Попробовать
Генератор libscanner формирует GS1 DataMatrix с ведущим FNC1 и разделителями в нужных местах; попробовать можно в онлайн-генераторе на сайте, исходники — на GitHub.
Ридер бережёт разделитель на всех выходах: в Python Code.text — bytes, и GS остаётся байтом 0x1D; командная строка dmr печатает его как <GS>, чтобы он не потерялся при копировании. Настройка requireGs1 отсекает коды без структуры GS1. Установка — pip install libscanner-dmr, проверка по фото — онлайн-сканер.
«Честный знак» — товарный знак ООО «Оператор-ЦРПТ», «GS1» — GS1 AISBL; продукт не связан с их владельцами и ими не сертифицирован.