Книга знаний

1С:Предприятие / Приемы программирования / Обмен данными, УРБД

Чтение и запись текстовых файлов XML средствами 1С:Предприятие 7.7

В этой статье приведены примеры последовательного (не нагружающего память) чтения и записи текстовых файлов в формате XML средствами встроенного языка (без внешних компонент). Автор статьи:
Последняя редакция №1 от 25.01.06
URL: http://kb.mista.ru/article.php?id=73

Приведу примеры выгрузки и загрузки многоуровневого (с группами) справочника номенклатуры. Примеры подробно комментированы, и в конце статьи есть ссылка на работающую конфигурацию для 1С:Предприятие 7.7. Почему не 8.0? Потому что в 8.0 похожий XML-парсер уже есть. Вы найдете почти полное сходство между этим стандартным парсером XML и приведенными ниже библиотечными функциями (вы можете вынести их, например, в глобальный модуль, а можете этого не делать).

Почему я избегаю использовать штатные средства 1С 7.7? Потому что они потребляют память примерно 10-кратно по сравнению с объемом исходного файла XML (используют объектную модель документа DOM). Это значит, что по мере роста файла XML будут расти "тормоза", вплоть до момента, когда память не будет исчерпана полностью (либо до момента, когда загрузка уйдет "в своп").

Конечно, внешней компонентой делать то же самое намного аккуратнее, и соблюдается принцип объектной ориентированности. Пример компоненты, сделанной средствами .NET приведен в этой статье:
Книга знаний: Написание внешних компонент для 1С на VB.NET и C#
Однако, если внешние компоненты по каким-то причинам неприменимы (постоянно сталкиваюсь со страхом перед внешними компонентами, который я назвал бы "компонентофобия"), то примерно то же самое делает и приведенный ниже программный код.

Кроме того, DOM намного сложнее (по сравнению, например, с файлами DBF) - поэтому приходится сталкиваться еще и с XML-фобией.

Имена функций я сделал такие же, как в 1С:Предприятие 8.0. Я предполагаю, что мало кто страдает "восьмеркофобией", и сможет хотя бы не впадать в глубокий ступор от использованных механизмов выгрузки и загрузки сведений (на примере справочника товаров). Тем более, что они занимают меньше строк кода, чем можно было бы предположить: фактически, парсер предельно прост.



//Ограничения на входящие файлы XML:                                                                              //XML          
//1) В первой строке обязателен заголовок XML вида <?xml version="1.0" encoding="windows-1251"?>                  //XML
//2) Каждый тег должен быть записан в отдельной строке. Возможны отступы от начала строки и пустые строки.        //XML
//3) Значения атрибутов - строго в двойных кавычках.                                                              //XML
//4) Не поддерживаются текстовые значения (любые значения можно передавать только через атрибуты).                //XML
//5) Комментарии и "подобные им" элементы XML не поддерживаются.                                                  //XML
                                                                                                                  //XML
перем xml_fso;                                                                                                    //XML
перем xml_file;                                                                                                   //XML
перем xml_ИмяТега;                                                                                                //XML
перем xml_СписокАтрибутов;                                                                                        //XML
перем xml_сз;                                                                                                     //XML
                                                                                                                  //XML
///////////////////////////////////////////////////////////////////////                                           //XML
//Открывает XML-файл в режиме "только чтение"                                                                     //XML
Процедура xml_ОткрытьФайл(прм_ИмяФайла)                                                                           //XML
//прм_ИмяФайла - имя файла XML (укажите полный путь и расширение .XML).                                           //XML
                                                                                                                  //XML
        xml_fso=СоздатьОбъект("Scripting.FileSystemObject");                                                      //XML
        xml_file=xml_fso.OpenTextFile(прм_ИмяФайла, 1, 0, 0); //Открываем файл в режиме "только чтение"           //XML
        стр=xml_file.ReadLine(); //Читаем заголовок XML вида <?xml version="1.0" encoding="windows-1251"?>        //XML
        Если Найти(стр,"<?xml")=0 Тогда                                                                           //XML
                Сообщить("Неправильный файл XML "+прм_ИмяФайла,"!"); а=10/0;                                      //XML
        КонецЕсли;                                                                                                //XML
        Если Найти(стр,"windows-1251")=0 Тогда                                                                    //XML
                Сообщить("Требуется кодировка windows-1251 файла XML "+прм_ИмяФайла,"!"); а=10/0;                 //XML
        КонецЕсли;                                                                                                //XML
        xml_СписокАтрибутов=СоздатьОбъект("СписокЗначений");                                                      //XML
        xml_сз=СоздатьОбъект("СписокЗначений");                                                                   //XML
КонецПроцедуры  // xml_ОткрытьФайл                                                                                //XML
                                                                                                                  //XML
///////////////////////////////////////////////////////////////////////                                           //XML
//Считывает следующий тег XML.                                                                                    //XML
//Возвращает 1, если тег прочитан и 0 - если был достигнут конец файла.                                           //XML
//Заполняет переменную xml_ИмяТега именем считанного тега в формате "<ИмяТега>"                                   //XML
Функция xml_Прочитать(прм_ОжидаемыеТеги="")                                                                       //XML
//Если вы используете параметр прм_ОжидаемыеТеги, то заполните его списком тегов                                  //XML
//через запятую, которые могут быть считаны в данный момент (это позволяет проверять                              //XML
//структуру файла XML и сделать более ясным считывающий код).                                                     //XML
                                                                                                                  //XML
        xml_СписокАтрибутов.УдалитьВсе();                                                                         //XML
        стр="";                                                                                                   //XML
        Пока стр="" Цикл                                                                                          //XML
                Если xml_file.AtEndOfStream=1 Тогда                                                               //XML
                        Возврат 0; //тег не был прочитан, т.к. достигнут конец файла                              //XML
                КонецЕсли;                                                                                        //XML
                стр=СокрЛП(xml_file.ReadLine());                                                                  //XML
        КонецЦикла; //цикл, чтобы пропустить пустые строки                                                        //XML
                                                                                                                  //XML
                                                                                                                  //XML
        Если Найти(стр,"=")=0 Тогда //Случай, когда нет атрибутов                                                 //XML
                xml_ИмяТега=СокрЛП(стр);                                                                          //XML
        Иначе                                                                                                     //XML
                //Разбиваем тег на структуру, удобную для ИзСтрокиСРазделителями()                                //XML
                стр=СтрЗаменить(стр,"""",""",""");                                                                //XML
                стр=""""+стр+"""";                                                                                //XML
                                                                                                                  //XML
                сз=xml_сз;                                                                                        //XML
                сз.УдалитьВсе();                                                                                  //XML
                сз.ИзСтрокиСРазделителями(стр);                                                                   //XML
                                                                                                                  //XML
                //В первом элементе списка у нас и имя тега, и имя первого атрибута. Разделим их.                 //XML
                зн=сз.ПолучитьЗначение(1);                                                                        //XML
                поз=Найти(зн , " ");                                                                              //XML
                xml_ИмяТега =СокрЛП(Лев(зн, поз)); //Выделим имя тега                                             //XML
                xml_ИмяТега =xml_ИмяТега+">";                                                                     //XML
                                                                                                                  //XML
                //Выделим и обновим имя первого атрибута                                                          //XML
                зн =СокрЛП(Сред(зн, поз));                                                                        //XML
                сз.УстановитьЗначение(1,зн);                                                                      //XML
                                                                                                                  //XML
                //Удаляем завершающий элемент списка /> или >                                                     //XML
                сз.УдалитьЗначение(сз.РазмерСписка());                                                            //XML
                                                                                                                  //XML
                //Переводим наш список значений в более удобный формат                                            //XML
                i=1; //позиция в списке сз                                                                        //XML
                рс=сз.РазмерСписка();                                                                             //XML
                Пока i<=рс Цикл                                                                                   //XML
                        имя=сз.ПолучитьЗначение(i);                                                               //XML
                        имя=СокрЛП(СтрЗаменить(имя,"=",""));                                                      //XML
                        зн=сз.ПолучитьЗначение(i+1);                                                              //XML
                        //Заменяем спецсимволы XML                                                                //XML
                        зн=СтрЗаменить(зн,"&quot;","""");                                                         //XML
                        зн=СтрЗаменить(зн,"&apos;","'");                                                          //XML
                        зн=СтрЗаменить(зн,"&lt;","<");                                                            //XML
                        зн=СтрЗаменить(зн,"&gt;",">");                                                            //XML
                        зн=СтрЗаменить(зн,"&amp;","&");                                                           //XML
                        xml_СписокАтрибутов.ДобавитьЗначение(зн,имя);                                             //XML
                        i=i+2;                                                                                    //XML
                КонецЦикла;                                                                                       //XML
        КонецЕсли;                                                                                                //XML
                                                                                                                  //XML
        Если ПустаяСтрока(прм_ОжидаемыеТеги)=0 Тогда                                                              //XML
                //Проверяем наличие имени тега в списке ожидаемых тегов (если надо контролировать структуру)      //XML
                Если Найти(прм_ОжидаемыеТеги,xml_ИмяТега)=0 Тогда                                                 //XML
                        Сообщить("Неожиданный тег "+xml_ИмяТега+" в строке "+xml_file.line); a=10/0;              //XML
                КонецЕсли;                                                                                        //XML
        КонецЕсли;                                                                                                //XML
                                                                                                                  //XML
        Возврат 1; //успешное чтение тега                                                                         //XML
КонецФункции    // xml_Прочитать                                                                                  //XML
                                                                                                                  //XML
///////////////////////////////////////////////////////////////////////                                           //XML
//Получает значение атрибута считанного тега по имени атрибута.                                                   //XML
//Если надо получить атрибут по его номеру, читайте список значений xml_СписокАтрибутов                           //XML
                                                                                                                  //XML
Функция xml_ПолучитьАтрибут(прм_ИмяАтрибута)                                                                      //XML
        Возврат xml_СписокАтрибутов.Получить(прм_ИмяАтрибута);                                                    //XML
КонецФункции    // xml_ПолучитьАтрибут                                                                            //XML
                                                                                                                  //XML
///////////////////////////////////////////////////////////////////////                                           //XML
//Закрывает открытый файл XML. По завершении работы с файлом его необходимо закрыть.                              //XML
Функция xml_Закрыть()                                                                                             //XML
        xml_file.Close();                                                                                         //XML
КонецФункции    // xml_Закрыть                                                                                    //XML
                                                                                                                  
                                                                                                                  
                                                                                                                  
//*******************************************                                                                     
//Тестовая процедура, которая считывает XML файл в справочник Товаров.                                            
Процедура Выполнить()                                                                                             
        стрИмяФайла=КаталогИБ()+"XML\tovar.xml";                                                                  
                                                                                                                  
        спр=СоздатьОбъект("Справочник.Товары");                                                                   
        НачатьТранзакцию();                                        //Для ускорения                                
        сч=0;                                                      //Для ускорения                                
                                                                                                                  
        Сообщить("Начало чтения XML: "+ТекущееВремя());                                                           
        xml_ОткрытьФайл(стрИмяФайла);
        
        xml_Прочитать("<Товары>"); 
        
        спрР=СоздатьОбъект("Справочник.Товары");
        
        Пока xml_Прочитать("<Элемент>,</Товары>")=1 Цикл
                сч=сч+1;                                               //Для ускорения
                Если сч%1000=0 Тогда                                   //Для ускорения
                        ЗафиксироватьТранзакцию();                         //Для ускорения
                        НачатьТранзакцию();                                //Для ускорения
                КонецЕсли;                                             //Для ускорения
                
                Если xml_ИмяТега="</Товары>" Тогда 
                        Прервать;
                КонецЕсли;
                
                Код=Число(xml_ПолучитьАтрибут("Код"));
                Наименование=xml_ПолучитьАтрибут("Наименование");
                Единица=xml_ПолучитьАтрибут("Единица");
                Цена=Число(xml_ПолучитьАтрибут("Цена"));
                ЭтоГруппа=Число(xml_ПолучитьАтрибут("ЭтоГруппа"));
                КодГруппы=Число(xml_ПолучитьАтрибут("Группа"));
                
                спрР.НайтиПоКоду(КодГруппы,0);
                
                Если спр.НайтиПоКоду(Код)=0 Тогда
                        спр.ИспользоватьРодителя(спрР.ТекущийЭлемент());
                        Если ЭтоГруппа=1 Тогда
                                спр.НоваяГруппа();
                        Иначе
                                спр.Новый();
                        КонецЕсли;
                        спр.Код = Код;
                КонецЕсли;
                
                спр.Родитель=спрР.ТекущийЭлемент();
                
                спр.Наименование = Наименование;
                спр.Цена = Цена;
                спр.Единица = Единица;
                спр.Записать();
                        
                
        КонецЦикла;     //по элементам XML
        
        xml_Закрыть(); 
        Сообщить("Обработка завершена! "+ТекущееВремя(),"i");
        ЗафиксироватьТранзакцию();                                 //Для ускорения
        
    
КонецПроцедуры



Это был пример обработки, которая читает файл XML, и создает многоуровневый справочник товаров.
А теперь приведу пример записи того же самого файла XML.

перем xml_fso;
перем xml_file;
перем xml_СтекТегов;
перем xml_ТегОткрыт;
перем xml_Отступы;


///////////////////////////////////////////////////////////////////////
//Открывает файл XML в режиме записи. Если файл существовал, перезаписывает его.
//Принимает параметр прм_ИмяФайла - имя файла (укажите полный путь и расширение .XML)
Процедура xml_СоздатьФайл(прм_ИмяФайла)
    xml_fso=СоздатьОбъект("Scripting.FileSystemObject");
    xml_file=xml_fso.CreateTextFile(прм_ИмяФайла, -1, 0); //создать файл, перезаписывая существующий
    xml_file.WriteLine("<?xml version=""1.0"" encoding=""windows-1251""?>"); //Пишем заголовок XML
    xml_СтекТегов=СоздатьОбъект("СписокЗначений");
    xml_Отступы="";
    xml_ТегОткрыт=0;
КонецПроцедуры    

///////////////////////////////////////////////////////////////////////
//Записывает начало элемента (тега XML). Имя можно указывать в угловых скобках.
Процедура xml_ЗаписатьНачалоЭлемента(прм_ИмяТега)
    перем стр;
    
    Если xml_ТегОткрыт=1 Тогда
        xml_ТегОткрыт=0;
        xml_file.WriteLine(">");
        xml_Отступы=xml_Отступы+"  ";
    КонецЕсли;
    
    стр=прм_ИмяТега;
    стр=СтрЗаменить(стр, "<", "");
    стр=СтрЗаменить(стр, ">", "");
    
    xml_СтекТегов.ДобавитьЗначение(стр);
    xml_file.Write(xml_Отступы+"<"+стр);
    xml_ТегОткрыт=1;
КонецПроцедуры    

///////////////////////////////////////////////////////////////////////
//Записывает атрибут (параметр) тега XML.
Процедура xml_ЗаписатьАтрибут(прм_ИмяАтрибута, прм_ЗначениеАтрибута)
    Если xml_ТегОткрыт=0 Тогда                                                                           
       Сообщить("Перед записью атрибута необходимо записать начало элемента!","!"); а=10/0;                                  
    КонецЕсли;                                                                                                
    
    
    стр=прм_ЗначениеАтрибута;
    стр=СтрЗаменить(стр, "&", "&amp;");
    стр=СтрЗаменить(стр, """", "&quot;");
    стр=СтрЗаменить(стр, "<", "&lt;");
    стр=СтрЗаменить(стр, ">", "&gt;");
    стр=СтрЗаменить(стр, "'", "&apos;");
    xml_file.Write(" "+прм_ИмяАтрибута+"="+""""+стр+"""");
КонецПроцедуры    // xml_ЗаписатьЗаписатьАтрибут

///////////////////////////////////////////////////////////////////////
//Записывает конец элемента (тега XML). Имя закрываемого тега можно указывать в угловых скобках, 
//а можно - не указывать вовсе.

Процедура xml_ЗаписатьКонецЭлемента(прм_ОжидаемоеИмяТега="")
    перем стрИмяТега, а;
    Если xml_СтекТегов.РазмерСписка()<1 Тогда
        Сообщить("Попытка закрыть неоткрытый элемент!","!"); а=10/0;                                      
    КонецЕсли;
    
    стрИмяТега=xml_СтекТегов.ПолучитьЗначение(xml_СтекТегов.РазмерСписка());
    
    Если ПустаяСтрока(прм_ОжидаемоеИмяТега)=0 Тогда
        стр=прм_ОжидаемоеИмяТега;
        стр=СтрЗаменить(стр, "<", "");
        стр=СтрЗаменить(стр, ">", "");
        стр=СтрЗаменить(стр, "/", "");
        
        Если стр<>стрИмяТега Тогда
            Сообщить("Ожидается имя тега "+стр+", а закрыто "+стрИмяТега,"!"); а=10/0;                                      
        КонецЕсли;
    КонецЕсли;
    
    
    xml_СтекТегов.УдалитьЗначение(xml_СтекТегов.РазмерСписка());
    Если xml_ТегОткрыт=1 Тогда
        xml_ТегОткрыт=0;
        xml_file.WriteLine("/>");
        Возврат;
    КонецЕсли;
    xml_Отступы=лев(xml_Отступы, СтрДлина(xml_Отступы)-2);
    xml_file.WriteLine(xml_Отступы+"</"+стрИмяТега+">");
    
КонецПроцедуры    // xml_ЗаписатьКонецЭлемента


///////////////////////////////////////////////////////////////////////
//Закрывает открытый файл XML. После окончания работы с файлом его необходимо закрыть.
Функция xml_Закрыть()
    xml_file.Close();
    Если xml_СтекТегов.РазмерСписка()<>0 Тогда
        Сообщить("Имеются незакрытые элементы XML!","!"); а=10/0;                                      
    КонецЕсли;
КонецФункции    // xml_Закрыть

//*******************************************
Процедура Выполнить()
    стрИмяФайла=КаталогИБ()+"XML\tovar_out.xml";
    Сообщить("Начало записи: "+стрИмяФайла);
    Сообщить("Время начала: "+ТекущееВремя());
    xml_СоздатьФайл(стрИмяФайла);
    xml_ЗаписатьНачалоЭлемента("<Товары>");
      спр=СоздатьОбъект("Справочник.Товары");
      спр.ВыбратьЭлементы();
      Пока спр.ПолучитьЭлемент()=1 Цикл  
          xml_ЗаписатьНачалоЭлемента("<Элемент>");
          xml_ЗаписатьАтрибут("Код", спр.Код);  
          xml_ЗаписатьАтрибут("Наименование", СокрЛП(спр.Наименование));  
          Если спр.ЭтоГруппа()=1 Тогда
              xml_ЗаписатьАтрибут("ЭтоГруппа", "1");  
          Иначе
              xml_ЗаписатьАтрибут("Единица", СокрЛП(спр.Единица));  
              xml_ЗаписатьАтрибут("Цена", СокрЛП(спр.Цена));  
          КонецЕсли;
          Если ПустоеЗначение(спр.Родитель)=0 Тогда
              xml_ЗаписатьАтрибут("Группа", спр.Родитель.Код);
          КонецЕсли;  
          xml_ЗаписатьКонецЭлемента("</Элемент>");
      КонецЦикла;
    xml_ЗаписатьКонецЭлемента("</Товары>");
    xml_Закрыть();
    Сообщить("Конец записи: "+ТекущееВремя());
КонецПроцедуры



Как видим, весь алгоритм записи файла XML занимает 100 строк, а весь алгоритм чтения (парсинга) XML - 116 строк (учитывая комментарии и пустые строчки, которые я добавил в код для ясности).

Чтение и запись XML происходит намного быстрее, и не потребляет память, чем это делает парсер DOM (от которого, напомню, фирма 1С отказалась в версии 1С:Предприятие 8.0, и применила более простые средства последовательного доступа к XML). Имена библиотечных функций, приведенных выше я сделал такими же, как в 8-ке. Удалось даже, на мой взгляд, несколько улучшить алгоритм.

Описание | Рубрикатор | Поиск | ТелепатБот | Захваченные статьи | Установки | Форум
© Станислав Митичкин (Волшебник), 2005-2025 | Mista.ru

Яндекс.Метрика