Вытаскиваем метаданные из буфера обмена 1С

25.11.12

Разработка - Механизмы платформы 1С

Однажды мне стало интересно, как 1С:Предприятие хранит в буфере обмена метаданные при их копировании в конфигураторе. Решил провести маленькое исследование. :)

Скачать файлы

Наименование Файл Версия Размер
clipb_1с.wlua
.wlua 0,56Kb
16
.wlua 0,56Kb 16 Скачать
Парсер1С8.epf
.epf 8,29Kb
9
.epf 8,29Kb 9 Скачать

Буфер обмена Windows поддерживает несколько стандартных форматов данных. Например текст и точечные рисунки. Кроме того, приложение может зарегистрировать свой собственный формат и скорее всего 1С:Предприятие пользуется этой возможностью.

Давайте проверим это! Узнать, какие форматы в данный момент содержит буфер обмена, можно с помощью стандартной утилиты Windows 2k/XP clipbrd.exe

Запускаем утилиту и давим ctrl+c на любом объекте дерева метаданных в конфигураторе:

clipbrd.exe

Что мы видим?

Я копировал реквизиты документа, и платформа поместила в буфер текст "Реквизиты". Кроме того, в меню "Вид" помимо стандартных форматов присутствуют 4 формата с префиксом "1С". Видимо это то что нам нужно :)

Плохо только, что эти форматы недоступны для просмотра. Что в общем логично, т.к. внутреннее их устройство "знает" только платформа 1С.

Чтобы увидеть содержимое нестандартных форматов нам понадобится более функциональная утилита. Например CLCL, которая умеет показывать неизвестные ей форматы в шестнадцатиричном виде. Скачать ее можно по этой ссылке: http://www.nakka.com/soft/clcl/index_rus.html

CLCL при запуске сворачивается в трей с иконкой в виде канцелярской скрепки. Открыть основное окно утилиты можно щелчком на этой скрепке.

ОК. Что нам показывает CLCL?

CLCL

Наиболее интересен формат "1C:MD8 Data". У него говорящее название и данных он содержит больше, чем все остальные форматы, вместе взятые. Внутри явно текст, только CLCL не знает кодировку. Щелкаем правой кнопкой мыши на подопытном формате, выбираем "сохранить как", и сохраняем в текстовый файл.

1С:MD8 Data

Открываем в любимом текстовом редакторе (на скрине Sublime Text 2) и видим, что это действительно текст. Кодировка "родная" для платформы 1С UTF-8 с маркером порядка байтов (BOM)

Итак, перед нами метаданные (в данном случае реквизиты документа). Судя по наличию фигурных скобочек можно догадаться, что это результат работы внутреннего сериализатора платформы 1С. Такой же формат возвращает метод глобального контекста ЗначениеВСтрокуВнутр.

Структура текста довольно простая. Это простой список внутри фигурных скобок. Элементы списка разделяются запятыми. В качестве элементов списка выступают: числа, строки в кавычках, UID'ы и вложенные списки.

Довольно легко можно написать парсер. Например так:

Функция РазобратьТекст(Источник) Экспорт
   
Дерево = Новый ДеревоЗначений;
   
Дерево.Колонки.Добавить("Значение");
   
ТекущаяСтрока = Дерево.Строки.Добавить();
   
ТекущаяСтрока = ТекущаяСтрока.Строки.Добавить();
   
ТекущийРодитель = ТекущаяСтрока.Родитель;
   
ИсходныйТекст = Источник.ПолучитьТекст();
   
КоличествоСимволов = СтрДлина(ИсходныйТекст);
   
ТекущийСимвол = "";
   
Позиция = 0;
    Пока
ТекущийСимвол <> "{" И Позиция < КоличествоСимволов Цикл
       
Позиция = Позиция + 1;
       
ТекущийСимвол = Сред(ИсходныйТекст, Позиция, 1);
    КонецЦикла;
    Если
ТекущийСимвол = "{" Тогда
       
Буфер = "";
       
РежимЧтенияСтроки = Ложь;
        Пока
Позиция < КоличествоСимволов Цикл
           
Позиция = Позиция + 1;
           
ТекущийСимвол = Сред(ИсходныйТекст, Позиция, 1);
            Если НЕ
РежимЧтенияСтроки И ТекущийСимвол = "{" Тогда
               
ТекущийРодитель = ТекущаяСтрока; ТекущийРодитель.Значение = "{...}";
               
ТекущаяСтрока = ТекущаяСтрока.Строки.Добавить();
            ИначеЕсли НЕ
РежимЧтенияСтроки И ТекущийСимвол = "," Тогда
                Если
Буфер <> "" Тогда
                   
ТекущаяСтрока.Значение = Буфер;
                   
Буфер = "";
                КонецЕсли;
               
ТекущаяСтрока = ТекущийРодитель.Строки.Добавить();
            ИначеЕсли НЕ
РежимЧтенияСтроки И ТекущийСимвол = "}" Тогда
                Если
Буфер <> "" Тогда
                   
ТекущаяСтрока.Значение = Буфер;
                   
Буфер = "";
                КонецЕсли;
               
ТекущаяСтрока = ТекущийРодитель;
               
ТекущийРодитель = ТекущаяСтрока.Родитель;
            ИначеЕсли
ТекущийСимвол = """" Тогда
               
РежимЧтенияСтроки = НЕ РежимЧтенияСтроки;
               
Буфер = Буфер + ТекущийСимвол;
            ИначеЕсли
РежимЧтенияСтроки Тогда
               
Буфер = Буфер + ТекущийСимвол;
            ИначеЕсли НЕ
ПустаяСтрока(ТекущийСимвол)
                ИЛИ (
ТекущийСимвол = " ") Тогда
               
Буфер = Буфер + ТекущийСимвол;
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;
    Возврат
Дерево;
КонецФункции

Парсер1С8х

Теперь у нас есть возможность "пощупать" структуру файла :)

На скрине видно, что корневой список содержит два вложенных списка. В первом из них хранится количество (первый элемент) и список реквизитов. Каждый реквизит предсталяет из себя структуру свойств. Т.е. все свойства реквизита, которые можно изменить в конфигураторе. Расположение свойств в структуре можно выяснить простым сравнением файлов до и после изменения этих самых свойств.

Пришло время воспользоваться нашими знаниями!

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

Писать скрипт будем на языке Lua
Скачать дистрибутив для Windows можно по этой ссылке: http://code.google.com/p/luaforwindows/downloads/list

Кроме того нам нужна библиотека для работы с буфером обмена: http://files.luaforge.net/releases/jaslatrix/clipboard/1.0.0 
(установить можно простым копированием clipboard.dll в каталог "...\Lua\5.1\clibs\")

Язык Lua был выбран не просто так. Дело в том, что в Lua основным типом данных является хэш-таблица (в 1С аналогом является соответствие), которая имеет очень похожий на нашу структуру конструктор. Выглядит это так:

tbl = {1, 2, "three", {4, 5}}

Таблицу можно обойти циклом:

for key, value in pairs(tbl) do
 print(key, value)
end

Проверить, как работает этот код, можно с помощью онлайн интерпретатора: http://repl.it/Emn/1

На языке 1С это выглядело бы так:

Таблица = Новый Соответствие;
Таблица[1] = 1;
Таблица[2] = 2;
Таблица[3] = "three";
Таблица[4] = Новый Соответствие;
Таблица[4][1] = 4;
Таблица[4][2] = 5;

Для Каждого
Элемент Из Таблица Цикл
   
Сообщить("" + Элемент.Ключ + " " + Элемент.Значение);
КонецЦикла;

Т.е. Lua автоматически назначает целочисленные ключи, начиная с единицы. Таким образом, луашная таблица может эмулировать массив (настоящих массивов в Lua нет). Ключи можно и явно указывать, но нам сейчас это не нужно. Интересующиеся могут почитать документацию: http://www.lua.ru/doc/

Итак, давайте для начала вытащим из буфера обмена текст в формате 1С:MD8 Data

require'clipboard'-- подключаем библиотеку
format={}-- создаем пустую таблицу для хранения соответствия [ИмяФормата - КодФормата]
for k,v in ipairs(clipboard.getformats()or{})do
    formatname = clipboard.formatname(v)
    if formatname then
        format[formatname]= v
    end
end

data = clipboard.getdata(format["1C:MD8 Data"]) -- получаем данные

if data then
    print(data)
end

Теперь нужно заставить Lua работать с этими данными как с родной хэш-таблицей. В этом деле нам поможет одна луашная фича. У нас есть возможность динамически загрузить функцию из строки содержащей луашный код. Например так:

f = loadstring("i = i + 1") -- получаем функцию 
i 0
f()print(i--> выводит 1
f()print(i--> выводит 2

Чтобы функция возвращала значение, нужно добавить return:

f = loadstring("return i + 1") -- получаем функцию возвращающую значение
i 0
i = f(); print(i) --> выводит 1
i = f(); print(i) --> выводит 2

Также функция может создать и вернуть таблицу:

f = loadstring("return {1, 2, 'three', {4, 5}}")
tbl = f()
for key, value in pairs(tbl) do
    print
(key, value)
end

Думаю, вы уже догадались что мы будем делать :)

f = loadstring("return"..data) -- прим.: две точки означают конкатенацию строк
tbl = f()

Но это не будет работать... :(

Lua не понимает UID'ы в тексте и кроме того она не знает UTF-8. К счастью, обе проблемы легко победить. Все UID'ы в тексте можно найти с помощью регулярного выражения и заменить на пустые таблицы {} например.
А кодировка UTF-8 по большому счету вообще не проблема, т.к. все важные для разбора символы кодируются в ней одним байтом и не отличаются от таковых в ASCII. Нам будет мешать только маркер порядка байтов (BOM), но мы можем просто проигнорировать первые три байта:

s = "123456"
print(s:sub(4)) --> 456

Когда мы наконец получим метаданные в виде луашной таблицы, можно будет обращаться к любому элементу примерно так:

print(tbl[1][2][2][1][2][2][2][3])

Этот код выведет на экран имя первого реквизита.

Чтобы узнать "адрес" нужного вам элемента, можете воспользоваться этим принтером таблиц:

function print_r (t, indent, done)
    done = done or {}
    indent = indent or ''
    local nextIndent -- Storage for next indentation value
    for key, value in pairs (t) do
        if type
(value) == "table" and not done[value] then
            nextIndent = nextIndent or
                (indent .. string.rep(' ',string.len(tostring(key))+2)) -- Shortcut conditional allocation
            done[value] = true
            print
(indent .. "[" .. tostring(key) .. "] => Table");
            print_r(value, nextIndent .. string.rep(' ',2), done)
        else
            print
(indent .. "[" .. tostring (key) .. "] => " .. tostring(value).."")
        end
    end
end

Эта функция выводит таблицу в таком виде (жирным выделен путь к имени первого реквизита):

[1] => Table
     [1] => 4
     [2] => Table
          [1] => Table
          [2] => Table
               [1] => Table
                    [1] => 3
                    [2] => Table
                         [1] => 25
                         [2] => Table
                              [1] => 2
                              [2] => Table
                                   [1] => 0
                                   [2] => Table
                                        [1] => 0
                                        [2] => 0
                                        [3] => Table
                                   [3] => ВалютаДокумента
                                   [4] => Table
                                        [1] => 1
                                        [2] => ru
                                        [3] => Валюта документа
                                   [5] => (Общ)
                              [3] => Table
                                   [1] => Pattern
                                   [2] => Table
                                        [1] => #
                                        [2] => Table
и т.д. 


Ну вот и все :) Готовый к использованию скрипт приложен к данной публикации.


Надеюсь, было интересно Smile

Публикация смежной тематики: Создание табличных частей объектов конфигурации

См. также

Поинтегрируем: сервисы интеграции – новый стандарт или просто коннектор?

Обмен между базами 1C Администрирование СУБД Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

В платформе 8.3.17 появился замечательный механизм «Сервисы интеграции». Многие считают, что это просто коннектор 1С:Шины. Так ли это?

11.03.2024    4525    dsdred    53    

71

Как готовить и есть массивы

Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Все мы используем массивы в своем коде. Это один из первых объектов, который дают ученикам при прохождении обучения программированию. Но умеем ли мы ими пользоваться? В этой статье я хочу показать все методы массива, а также некоторые фишки в работе с массивами.

24.01.2024    5294    YA_418728146    25    

63

Планы обмена VS История данных

Обмен между базами 1C Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Вы все еще регистрируете изменения только на Планах обмена и Регистрах сведений?

11.12.2023    6409    dsdred    36    

111

1С-ная магия

Механизмы платформы 1С Бесплатно (free)

Язык программирования 1С содержит много нюансов и особенностей, которые могут приводить к неожиданным для разработчика результатам. Сталкиваясь с ними, программист начинает лучше понимать логику платформы, а значит, быстрее выявлять ошибки и видеть потенциальные узкие места своего кода там, где позже можно было бы ещё долго медитировать с отладчиком в поисках источника проблемы. Мы рассмотрим разные примеры поведения кода 1С. Разберём результаты выполнения и ответим на вопросы «Почему?», «Как же так?» и «Зачем нам это знать?». 

06.10.2023    18473    SeiOkami    46    

118

Дефрагментация и реиндексация после перехода на платформу 8.3.22

Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Начиная с версии платформы 8.3.22 1С снимает стандартные блокировки БД на уровне страниц. Делаем рабочий скрипт, как раньше.

14.09.2023    12088    human_new    27    

74

Валидация JSON через XDTO (включая массивы)

WEB-интеграция Универсальные функции Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

При работе с интеграциями рано или поздно придется столкнуться с получением JSON файлов. И, конечно же, жизнь заставит проверять файлы перед тем, как записывать данные в БД.

28.08.2023    8821    YA_418728146    6    

141

Внешние компоненты Native API на языке Rust - Просто!

Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Внешние компоненты для 1С можно разработывать очень просто, пользуясь всеми преимуществами языка Rust - от безопасности и кроссплатформенности до удобного менеджера библиотек.

20.08.2023    6279    sebekerga    54    

94

Все скопируем и вставим! (Буфер обмена в 1С 8.3.24)

Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Рассмотрим новую возможность 8.3.24 и как её можно эффективно использовать

27.06.2023    15986    SeiOkami    31    

103
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. Oleg_nsk 277 25.11.12 14:51 Сейчас в теме
Познавательно. Но зачем это может понадобиться? Не могли бы в начале обозначить спектр задач где ваши научные исследования структуры буфера обмена 1с могут пригодится простому разработчику.
2. ilov_boris 163 25.11.12 16:40 Сейчас в теме
(1) я не очень понимаю что именно вы подразумеваете под словами "простой разработчик", но думаю что любому грамотному специалисту не составит труда найти практическое применение своим знаниям. Простейший вариант освещен в публикации (вытаскивание списка имен реквизитов для вставки в код например...)
Можно придумать кучу различных скриптов, помогающих в повседневной работе. Например можно написать скрипт, генерирующий код процедуры-шаблона для создания нового документа и заполнения всех его реквизитов. Скрипт будет брать необходимую информацию из буфера обмена, а обратно помещать код на встроенном языке.

Я не предлагаю конкретное решение, а только лишь показываю как это работает и как это можно использовать.
3. sytkosa 119 25.11.12 19:20 Сейчас в теме
В снегопате можно использовать такую схему. там как раз не хватает такого плана скриптов.
4. tango 506 25.11.12 19:40 Сейчас в теме
(3) 8SiriuS8, тут фишка как раз в том, что снегопат может покурить
ilov_boris; +1 Ответить
7. orefkov 1152 28.11.12 09:49 Сейчас в теме
(4)
А курить я больше года как бросил :)
6. orefkov 1152 28.11.12 09:48 Сейчас в теме
(3)
В снегопате это делается так:
Код
var file = metadata.current.rootObject.childObject("Документы", "АвансовыйОтчет").saveToFile()
file.seek(0, fsBegin)
Message(file.getString(dsUtf8))
Показать полностью

и все.
awa; bulpi; +2 Ответить
12. orefkov 1152 29.11.12 00:32 Сейчас в теме
И кстати да, в (6) описано было, как получить то, что в первой части статьи - внутреннее представление.
А список реквизитов там проще получается, без парсинга списка.
5. sstar90 26.11.12 16:31 Сейчас в теме
Плюс. Надо попробовать для общего развития
ilov_boris; +1 Ответить
8. vec435 15 28.11.12 22:51 Сейчас в теме
а если на основе Clcl сделать ВК то и использовать можно как снегопат
9. ilov_boris 163 28.11.12 23:55 Сейчас в теме
(8) vec435, ВК - это уже режим предприятия (да и зависимость от оного). А Снегопат и скрипты на Lua позволяют получить профит находясь в конфигураторе.
ВК и обработку несущую такой функционал конечно можно сделать, но это будет дико неудобно.
10. ilov_boris 163 29.11.12 00:14 Сейчас в теме
На этих выходных постараюсь еще пару скриптов сделать, чтобы больше осветить возможности.
Если у кого есть идеи, то высказывайтесь здесь. Попробую реализовать предложения :)

зы И еще... могу описать более подробно как установить и использовать Lua в связке с Sublime Text 2.
11. orefkov 1152 29.11.12 00:28 Сейчас в теме
(10)
Из идей - прикрутить это к autoit (где-то тут был на нем мини опенконф для восьмерки)
13. ilov_boris 163 29.11.12 00:33 Сейчас в теме
(11) orefkov, возможно я ошибаюсь, но в autoit вроде только текст из буфера обмена можно вытащить...
15. orefkov 1152 29.11.12 00:51 Сейчас в теме
(13)
Ну так потом можно текст как-то обработать, поместить в буфер и послать контрол v
(14)
http://infostart.ru/public/65526/ здесь упоминается
14. ilov_boris 163 29.11.12 00:43 Сейчас в теме
(11) "где-то тут был на нем мини опенконф для восьмерки"
Покажите где оно лежит плиз. Интересно однако :)
16. vec435 15 29.11.12 08:21 Сейчас в теме
при копирование объектов из конфы в конфу по Cntr+C копируется целиком объект.значит из буфера можно по-идее все вытащить.что-то вроде UnPack может получится прямо из конфигуратора. а обработать удобнее в предприятии.
17. orefkov 1152 29.11.12 08:50 Сейчас в теме
Кстати, посмотрел, что лежит в "1C:MD8 External Data"
Если преобразовать из base64, получим текст модуля документа, в формате, понимаемом v8unpuck.
Но мне вот что интересно.
Если я в одной конфе нажал Ctrl+C допустим на документе, а в другой Ctrl+V, то док вставляется весь - все формы, модули и т.п. Но в клипборде я что-то не вижу этой инфы, а только структуру метаданных, да модуль дока. Откуда это передается?

А если я скопировал док, а в другой конфе встал на узел "Последовательности" и нажал Ctrl+V, то добавляется новая последовательность, с именем как у дока, а реквизиты дока стают измерениями последовательности.
18. orefkov 1152 29.11.12 10:42 Сейчас в теме
+(17)
Хотя нет, погорячился насчет того, что в "1C:MD8 External Data" только модуль документа. Невнимательно я декодировал. Там для всех вложенных объектов метаданных все лежит в таком же 1Сном фигурном списке. Все формы, модули и т.д. и т.п. Надо просто аккуратно декодировать.
19. ilov_boris 163 29.11.12 11:06 Сейчас в теме
(18) Хотел уже было ответить вам, но вы меня опередили :)
Да, там все есть. И про модуль в External Data я знаю.

Кстати можно сделать скрипт, который выведет отчет по назначенным обработчикам в модулях объектов. (ПриЗаписи, ПередУдалением и т.д. и т.п.) На Lua с ее строковой либой должно быть элементарно. :)
21. orefkov 1152 29.11.12 11:33 Сейчас в теме
(19)
Обработчики умеешь получать и для толстых и для упр форм?
В снегопате было бы хорошо иметь эту инфу, тогда в обработчиках уже стал бы известен тип параметров.
22. ilov_boris 163 29.11.12 18:46 Сейчас в теме
(21) orefkov, не знаю :) нужно пробовать
20. ilov_boris 163 29.11.12 11:16 Сейчас в теме
Посмотрите еще как "Роли" закодированы. Забавно там :)
Использовать парсер писанный на 1С для такого объема уже не айс.
23. oberon355 15 02.12.12 11:30 Сейчас в теме
походу уже не осталось мест, куда не проникал еще пытливый взгляд 1с ника.
ilov_boris; +1 Ответить
24. alexandr1972_1 10.12.12 00:51 Сейчас в теме
Очень интересно, особенно в плане использования Lua. Поставим в закладки, потом разберёмся.
25. CaSH_2004 372 20.01.13 20:34 Сейчас в теме
Занятно, из областей применения возможно подойдет такая задача: тут встречалась задача что нужно новую обработку конвертировать из 8.2 в 8.1 или даже 8.0, предложено было решение копировать из конфигуратора 8.2 в конфигуратор 8.1/8.0, однако иногда были проблемы при переносе копированием, по всей видимости из-за разной структуры. Этот механизм мог бы помочь решить данную проблему, но по сути проще самому все нарисовать чем все изучить и запрограммировать.
Интересно, а обратного конвертера так никто и не придумал? Может UnPack справиться?
26. AnryMc 849 19.03.13 16:13 Сейчас в теме
??? ЗначениеВСтрокуВнутр ???
27. ilov_boris 163 19.03.13 16:20 Сейчас в теме
28. AnryMc 849 19.03.13 16:30 Сейчас в теме
(27) ilov_boris,

Половина описаной работы решается этим
29. ilov_boris 163 19.03.13 16:41 Сейчас в теме
(28) AnryMc, какая половина и каким образом?

ps ... или вы просто потроллить заглянули? ;)
30. Serg2000mr 311 17.03.23 19:14 Сейчас в теме
(0) Где теперь можно эту библиотеку clipboard-1.0.0-Lua51.zip найти?
Оставьте свое сообщение