Для тех, кому некогда: работает, качайте, ставьте плюсик и спасибо за внимание.
Люблю, целую, обнимаю.
Вместо вступления
Есть такая классная разработка - bsl_console.
Буквально на днях автор выложил новую версию, с синтакс-помощником внутри. Очень нужно, полезно и вообще респект, уважуха и низкий поклон.
Подключил, попробовал, понравилось - и тут вспомнил, что я-то хотел его в режиме предприятия, силами платформы, без внешних компонент и прочих подпорок. Ну что же...
Claude, ты там не устал бездельничать? Токены сами себя не сожгут...
Слона в комнате прятать не буду: большую часть работы сделал Claude Opus 5 - он же Opus, Клод, болванчик, ИИшечка и как ещё повернётся язык по ходу дела. Без такого помощника я бы за эту задачу не взялся: она не сложная, просто непонятно, чем кончится. Как это выглядело в работе - будет дальше.
Кроме спортивного интереса, практическая польза тут тоже есть, но лежит она не совсем там, где начиналось (bsl_console, PrintWizard): в замерах по ЧтениеZipФайла и в паре мест, где платформа ведёт себя неочевидно.
Ну а если по ходу чтения покажется, что автору нечем заняться, - ощущение верное, спорить не буду.
С чего началось
Синтакс-помощник в конфигураторе (ну вдруг кто не знал) - это тридцать девять мегабайт отборного shcntx_*.hbk в каталоге bin. Тот самый, по которому мы все живём, а смотрим на него в конфигураторе, ну или в EDT (если вы из наших).
Все мы его любим, ценим и очень скучаем, когда работаем с кодом в Infostart Toolkit.
Поэтому кто-то держит конфигуратор рядом, кто-то смотрит в интернет, хотя справка - вот она, рядом, "на диске, Карл". В конце концов, доколе...
Скажу честно: эта мысль приходила ко мне давно, ну года два-три точно, и ровно столько же я за неё не брался. Формат недокументированный (хотя вроде бы изученный), ковырять двоичный контейнер по вечерам - удовольствие на любителя. А распаковать zip в html и читать их - это, прямо скажем, не самый оптимальный путь. Поэтому всё как-то: то руки заняты, то голова.
Про то, что было до меня
Велосипед я не изобретал, а потому гордиться этим буду немного, не досаждая окружающим.
Секрет полишинеля: .hbk - контейнер того же формата, что .cf и .epf. Формат описан давно и подробно, статья про него на Инфостарте живёт с 2015 года. Утилиты для распаковки .hbk тоже есть, и не одна: от совсем древней консольной до просмотра с редактированием. Есть и расширения, которые показывают справку из Предприятия, - например Интерактивная справка по объектам 1С или Интерактивная справка [Alt+I], - как правило, через заранее подготовленную базу знаний или через внешние файлы.
Чем мне это не подошло: везде, где я смотрел, справка сначала распаковывается на диск, а потом уже показывается. Пятьдесят две тысячи файлов, семьдесят с лишним мегабайт и минута с лишним ожидания - и всё это ради того, чтобы прочитать одну статью про ТаблицаЗначений.Добавить. Хотелось иначе: открыть штатный файл платформы как есть, средствами самой 1С, ничего никуда не распаковывая и без внешних утилит с компонентами.
Что внутри .hbk
До таблицы сущностей всё знакомо: шестнадцать байт заголовка, дальше блоки с ascii-шапкой в 31 байт, сама таблица по смещению 47, по 12 байт на запись, имя сущности - в теле заголовка с 21-го байта в UTF-16LE. Сущности у справки свои, и в статьях про .cf их нет.
Сущностей семь, набор одинаковый у всех сорока пакетов, и по-настоящему нужны три:
- FileStorage - это сами статьи, валидный zip на 52 064 записи;
- PackBlock - оглавление;
- IndexPackBlock - предметный указатель.
Остальные четыре весят от 44 байт до пары сотен килобайт и служебные.
Нудно? Это Opus, он без "details" не умеет. Дальше начинается интересное.
Оглавление лежит в zip-записи, у которой нет центрального каталога.
Так вот, внутри PackBlock локальная запись deflate есть, а каталога и EOCD нет - контейнер их и не предполагал. Штатный ЧтениеZipФайла такой файл не открывает и, что характерно, ничего внятного при этом не говорит. Пришлось вокруг записи достраивать корректный архив руками: локальная запись как есть, к ней своя запись центрального каталога, следом EOCD. Дальше платформа работает как ни в чём не бывало. Сорок шесть миллисекунд на распаковку четырёх с половиной миллионов символов - приемлимо.
Указатель приятно удивил
Каждый элемент несёт не только имя и пути статей, но и номера соседей по алфавиту, отдельно для русского и английского. То есть в файле уже лежит готовый двусвязный список в алфавитном порядке, сортировать ничего не надо. И короткое имя (Структура), и полное имя с владельцем (ТаблицаЗначений.Добавить) - это два разных элемента, поэтому в конфигураторе находится и то, и другое.
А вот полнотекстового индекса в пакете нет.
Вообще. Ни в одном из сорока пакетов. Я кажется проверил все, потому что не поверил. Указатель конфигуратора, который лежит отдельно в профиле пользователя (%LOCALAPPDATA%\1C\1cv8\helpsynt.dat), тоже содержит только имена - слов из текстов статей в нём нет. Вывод: искать по именам можно мгновенно, готовые цепочки лежат прямо в файле, - а вот поиск по текстам статей стоит полного прохода по двадцати пяти тысячам html, кто бы его ни делал.
Но конфигуратор-то ищет моментально. Значит, чего-то мы не нашли. Поэтому поиска по текстам в обработке пока нет: перебор двадцати пяти тысяч статей стоит времени, а готовый индекс надо где-то держать. Если кому-то известно, чем это делает конфигуратор, - welcome to GitHub.
Место, ради которого стоило всё это писать
Хранилище статей - валидный zip на 52 064 элемента. Казалось бы, открывай да бери элемент по имени. Замерил, и вот что вышло. Абсолютные значения тут дело десятое - на другой машине они уедут все разом; смотреть надо на отношение между строками, а оно устойчивое. Условия одинаковые: платформа 8.3.27.2214, один и тот же архив, одни и те же пять статей, разбросанных по нему; в таблице - время на статью целиком, вместе с извлечением.
Как получен элемент + Время на статью
-
Извлечение прямо внутри Для Каждого ... Из Архив.Элементы (0,6 мс)
-
По ранее сохранённой ссылке на элемент (108 мс)
-
По индексу Архив.Элементы[Индекс] (329 мс)
-
Архив.Элементы.Найти(Имя) и извлечение (631 мс)
Читается это примерно так: платформа умеет быстро извлечь только тот элемент, который прямо сейчас является текущим в обходе коллекции. Всё остальное, как будто, ищется перебором. Разница между половиной миллисекунды и половиной секунды - это ровно разница между "элемент у меня в руках" и "элемент я где-то раздобыл".
Ссылку на сохраненный элемент, что был в обходе запоминать тоже не помогает.
На архиве из ста файлов вы этого не заметите никогда. На пятидесяти двух тысячах разница между "мгновенно" и "полсекунды на статью".
Полная распаковка, кстати, спасает: после неё статья читается за 0,15 мс. Но: стоит она семьдесят секунд и 71,4 МБ на диске. Для разового открытия справки - не наш вариант.
В итоге Найти в обработке не используется вовсе. Все самостоятельно: разбираю центральный каталог архива, беру из него смещение и размер записи, читаю ровно одну запись потоком. На прогретом файле это три-пять миллисекунд на статью, около двадцати - если до этого участка файла ещё никто не дотрагивался. Против шестисот тридцати.
Если вы работаете с большими архивами штатными средствами - возможно вам эта информация пригодится.
Как Opus джуном прикидывался
Если кто-то сходу скажет, чему равно выражение Формат(0, "ЧГ=0") - садитесь, пять. А вот Клод голову себе поломал, пока не догадался, что нужен ещё ЧН=0. Объяснение-то простое: ЧГ - это группировка разрядов, а представление нуля задаётся отдельно, параметром ЧН, и по умолчанию оно пустое. Ему нужен был "0", а приходила пустая строка.
Соль в том, где это вылезает. Смещение записи уходит в индекс через Формат, а ноль там ровно один - первая запись архива лежит по нулевому смещению. То есть из пятидесяти двух тысяч статей ломается ровно одна и, разумеется, не та, которую открываешь при отладке. Ну что же, бывает.
Но прежде чем восклицать "мы же говорили, они нас не заменят", - вот заметка от него же:
Для разбора двоичных данных через строки годится только `ISO-8859-1`: она отображает все 256 байт один в один. `windows-1251` этого не гарантирует, и вы получите тихо испорченные байты там, где не ждёте.
Так что, кожаные братья, всё же стоит бояться...
Где читать файл: клиент или сервер
Тут работу дважды развернуло на сто восемьдесят градусов, и оба раза разворачивал её я - причём второй раз сам себе наперекор. Вот что значит делать без спецификации. Следите за руками.
Клод начал по инерции: первый стенд гонялся в модуле управляемого приложения, толстым клиентом. Файл под рукой, ФайловыеПотоки и ЧтениеZipФайла работают, вопрос "а где этому положено исполняться" никто себе не задал. Код вышел клиентским просто потому, что стенд был клиентским.
Потом прилетело "Использование синхронных методов на клиенте запрещено!" - дважды. Конфигуратор при отладке ставит клиенту ключи строгой проверки, и весь файловый код под ними ложится. Реакция была предсказуемой и ленивой: унести всё на сервер. Тут прибежал чайка-архитектор (я):
ЧтениеZipФайла, ЧтениеТекста - согласен, они серверные - ты на сервер файл hbk таскать не планируешь? ))))
Внимательный читатель скажет: стопэ, они клиент-серверные. Да, да, да. Но я продолжал жечь напалмом: у любого подключения 1С есть сервер - хоть тонкий, хоть толстый, хоть веб. Раз .hbk всегда лежит в серверной установке (запомните это допущение, оно ещё выстрелит), то, может, ну её, клиентскую ветку? А в вебе другого пути и нет, и поэтому баста.
Где-то тут красная лампочка и загудела: на сервере в корпоративе вполне может стоять Linux, а файлы - оказаться недоступными. Значит, "всегда сервер" ломается ровно так же, как "всегда клиент".
Вывод получился третий. Вся работа с данными клиент-серверная: один и тот же метод компилируется и туда, и туда, а где читать - решается в рантайме, проверкой доступности файла. Сначала пробует клиент - пакет лежит рядом, это его же установка платформы; не нашёл, уходим на сервер; веб-клиент сразу на сервер, файлов он не видит вовсе. И там файловые методы не молчат заглушкой, а бросают исключение с именем метода: заглушка превращает "в вебе не работает" в "в вебе иногда пусто", а разбираться потом вам же. Служебная часть модуля и вовсе выключена препроцессором - иначе он под веб-клиент просто не соберётся.
Почему клиент первым, а не сервер, - вопрос замеров. Сервер это тот же самый код плюс накладные расходы вызова: в том прогоне статья через сервер выходила примерно вдвое дороже, а при открытии добавлялись семь мегабайт по проводу (оглавление 4,5 МБ и индекс 2,4 МБ). Цифры, понятно, зависят от сети и железа, но знак разницы от них не зависит. А промах клиента не стоит почти ничего: файла нет - ушли на сервер.
Ирония напоследок: строгий режим, с которого всё началось, стал главным инструментом проверки. Тонкий клиент с теми же ключами гарантированно загоняет код на серверную ветку, прогон толстым проверяет клиентскую - каждый запуск покрывает обе дороги.
Выводы
Specification first. Полчаса на спецификацию сэкономили бы мне два разворота, а Клоду - половину переписанного модуля.
И контрольный выстрел, уже пока писал статью. Мы ведь так и не проверили, лежит ли shcntx_*.hbk в серверной установке под Linux - предположили по тому, что на Windows он в том же bin, что и rphost.exe. Собрал версию обработки с выключенной клиентской веткой, открыл в рабочей базе на сервере (Debian, без графики) - "пакет справки не найден".
Полез разбираться, кто виноват: поиск или установка. Добавил в ту же сборку диагностику, и сервер ответил сам: пакетов справки рядом целых 648 штук, а вот по маскам shcntx_*, shlang_*, shquery_*, shclang_* - ноль. Ни синтакс-помощника, ни встроенного языка, ни языка запросов.
Мораль. "Всегда сервер" не просто рискованно - на обычной линуксовой серверной поставке оно не поехало бы вовсе, потому что читать там нечего. А веб-клиенту к такой базе справку показывать нечем, пока не положишь shcntx_*.hbk на сервер руками. Вот вам и цена непроверенного допущения: полдня рассуждений о том, где быстрее читать файл, которого на сервере попросту нет.
Проверка, которая гоняется сама
Это, конечно, не YAxUnit, да он тут и не нужен: проверка headless и запускается одной командой. Двадцать пять секунд, ни одного клика мышью, 36 самопроверок, отчёт текстом. Написал - прогнал - увидел "ОК 26" или "упало на 14" - починил - прогнал снова.
Стенд этот, если честно, не для меня: мне открыть форму и потыкать недолго. Он для Клода. Без способа узнать, что ты сломал, любой помощник остаётся генератором текста, похожего на код, - а с таким способом он гоняет цикл сам, и я подключаюсь не к "работает или нет", а к "правильно ли мы вообще это делаем".
И про цифры: в этой статье они перемеряны стендом, а не переписаны из чьего-то ответа.
Что получилось

Внешняя обработка на одну форму. Открывается через Файл - Открыть, пакет справки находит сама в каталоге платформы.
-
содержание - дерево с ленивой подгрузкой, 28 360 разделов и статей, узел разворачивается за миллисекунду-две;
-
индекс - поиск по началу русского или английского имени, с владельцем через точку, удачные запросы копятся в списке выбора;
-
статья - html как есть, ссылки внутри справки открываются в том же окне, внешние уходят браузеру;
-
навигация - назад, вперёд, вышестоящий и нижестоящий раздел, "найти в содержании" с раскрытием дерева до текущей статьи.
Про скорость скажу как есть, без округлений в свою пользу. Открытие пакета - это разовая цена: у меня на рабочей машине строка состояния показывает около 5,6-5,8 секунды, и на повторных открытиях столько же. На голом стенде, без формы и живой конфигурации, те же шаги укладываются в полторы секунды. Разброс объясняется просто: надо один раз прочитать с диска файл на 39 мегабайт, а дальше всё зависит от диска, машины, антивируса и того, что ещё крутится рядом. У кого-то будет три секунды, у кого-то восемь.
Дальше начинается то, ради чего всё затевалось, и там уже быстро при любом железе: узел дерева разворачивается за миллисекунду-две, статья показывается за единицы миллисекунд, повторно - мгновенно, поиск по имени - меньше десятой доли секунды. Память не разбухает: файл читается потоком, статьи достаются по одной.
Что дальше
До конфигураторского синтакс-помощника не дотягивает: поиска по текстам нет (см. выше про отсутствующий индекс), языка запросов и языка выражений СКД рядом тоже нет - они лежат в отдельных пакетах (объектная модель СКД, само собой, на месте, она часть синтакс-помощника).
Поэтому:
-
язык запросов и язык выражений СКД рядом с синтакс-помощником: это отдельные пакеты того же формата, работы там немного;
-
поиск по текстам статей. Полный проход по двадцати пяти тысячам html я один раз сделаю, а вот куда класть результат, чтобы не собирать его каждый раз, ещё думаю;
-
маски в указателе (Струк*);
-
ну и под macOS и Linux стоит проверить повнимательнее
Пы.сы.
Обработка и исходники лежат на GitHub, лицензия MIT. Собранный .epf - в релизах. Там же в docs подробно про формат .hbk и замеры, включая то, что в статью не влезло. И CLAUDE.md с правилами для болванчика - если интересно, как это выглядит на практике, посмотрите, там всё честно: и что можно, и куда не лезть.
Если найдёте, что я неправильно понял в формате, - пишите, поправлю. Формат недокументированный, всё, что выше, получено чтением живых файлов, а не откровением.
Проверено на следующих конфигурациях и релизах:
- Бухгалтерия предприятия, редакция 3.0, релизы 3.0.203.24
Вступайте в нашу телеграмм-группу Инфостарт