Каталог решений - Загрузка данных из файла .CSV

Загрузка данных из файла .CSV

Загрузка данных из файла .CSV

В наличии

Алгоритм для загрузки данных в учебную конфигурацию из файла с расширением CSV.

Категория:

Описание

Здравствуйте. Эта публикация является частью учебного процесса, так что не судите строго.

 

Задача — создать обработку для загрузки данных в учебную конфигурацию из файла с расширением CSV.

Учебная конфигурация — это простая база ведения домашней бухгалтерии.

Данные — это файл выгрузки из аналогичной андроид программы.

Итак приступим к решению.

Шаг 1. Прочитать файл с данными

Создаем обработку. В форме обработки создаем:

  • реквизит — строку "Имя Файла"
  • и команду "Прочитать". 

Для команды "Прочитать" сразу сделаем процедуры ПрочитатьНаКлиенте и ПрочитатьНаСервере и пока отложим их.

Нам необходимо, чтобы строка "Имя Файла" позволяла выбрать файл. Для этого создадим дополнительную процедуру 

  &НаКлиенте
  Процедура ИмяФайлаНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
  	ДиалогВыбора = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
  	ДиалогВыбора.Заголовок = "Выберите каталог";
  	
  	Если ДиалогВыбора.Выбрать() Тогда
  		ИмяФайла = ДиалогВыбора.ПолноеИмяФайла;
  	КонецЕсли;
  	каталогCSV=ДиалогВыбора.Каталог;
  	файлCSV=ИмяФайла;
  	файлCSV_ansi = ПерекодироватьФайлUTF8_ANSI(каталогCSV,файлCSV);
  	ИмяФайла=файлCSV_ansi;
  	
  КонецПроцедуры
  

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

  файлCSV=ИмяФайла;
  файлCSV_ansi = ПерекодироватьФайлUTF8_ANSI(каталогCSV,файлCSV);
  ИмяФайла=файлCSV_ansi;
  

А также пришлось добавить на форму соответствующую функцию

  Функция ПерекодироватьФайлUTF8_ANSI(каталогФайла, файлUTF8)
  
  	  исходныйФайл = файлUTF8;
  	  файлЧтение  = Новый ЧтениеТекста(исходныйФайл,КодировкаТекста.UTF8);    
  	  txt=файлЧтение.Прочитать();
  	  файлЧтение.Закрыть();
  
  	  ФайлANSI = СтрЗаменить(файлUTF8,".","_ansi.");
  	  файлЗапись = Новый ЗаписьТекста(ФайлANSI,КодировкаТекста.ANSI);    
  	  файлЗапись.Записать(txt);
  	  файлЗапись.Закрыть();
  	  
        Возврат ФайлANSI;
  КонецФункции
  

Теперь вернемся к процедуре "Прочитать", а именно к процедуре "ПрочитатьНаСервере" которую мы создали выше. Код процедуры состоит из таких частей:

Первая

  
  	ПеременИмяФайла=ИмяФайла;
  	
  	ЗагружаемыйФайл = Новый ТекстовыйДокумент;
  	ЗагружаемыйФайл.Прочитать(ПеременИмяФайла);
  	Разделитель = ",";
  	
  	Шапка = ЗагружаемыйФайл.ПолучитьСтроку(1);
  	
  	МассивКол = СтрРазделить(Шапка,",");         
  			
  	
  	Таблица = Новый ТаблицаЗначений;
  	
  	
  	Для Каждого ИмяСтолбца Из МассивКол Цикл
  		ИмяБезПробелов = СтрЗаменить(ИмяСтолбца," ",""); 
  		Таблица.Колонки.Добавить(ИмяБезПробелов);
  				
  	КонецЦикла;
  		
  	Для НомерСтроки=2 по ЗагружаемыйФайл.КоличествоСтрок() Цикл
  		
  		Строка = ЗагружаемыйФайл.ПолучитьСтроку(НомерСтроки);
  		МассивКол = СтрРазделить(Строка,Разделитель);
  		НоваяСтрочка= Таблица.Добавить();
  		
  		      	
  		
  		Для НомерСтолбца= 1 по МассивКол.Количество() Цикл
  			ТекущееЗначение = МассивКол[НомерСтолбца-1];
  			ИмяКолонки = Таблица.Колонки[НомерСтолбца-1].Имя;
  			НоваяСтрочка[ИмяКолонки] = ТекущееЗначение;
  			
  		КонецЦикла;
  		
  	КонецЦикла;
  

Здесь мы читаем данные с файла и добавляем их в таблицу значений. Хочу сразу предупредить, что таблица значений не предусматривает одинаковое название колонок. У меня как раз были такие, поэтому я не стал долго мучатся с этим и просто переименовал их. Однако подозреваю, что если чуть подумать и поднапрячься, то можно доработать код и решить эту проблему.

Вторая часть

  МассивТипаВыбора = Новый Массив;
  	МассивТипаВыбора.Добавить(Тип("ТаблицаЗначений"));
  	ОписаниеТипаВыбора = Новый ОписаниеТипов(МассивТипаВыбора);
  	МассивРеквизитов = Новый Массив;
  	МассивРеквизитов.Добавить(Новый РеквизитФормы("ТаблицаCSV",ОписаниеТипаВыбора,));
  	
  	Для Каждого Ст ИЗ Таблица.Колонки Цикл
  		Если Ст.Имя="" Тогда Ст.Имя="Пустая";
  		КонецЕсли;
  		МассивРеквизитов.Добавить(Новый РеквизитФормы(Ст.Имя, Новый ОписаниеТипов("Строка") , "ТаблицаCSV"));
  		
  	КонецЦикла;
  
  	
  	ИзменитьРеквизиты(МассивРеквизитов);
  	
  	Таб = Элементы.Добавить("ТаблицаCSV", Тип("ТаблицаФормы"));
  	Таб.ПутьКДанным = "ТаблицаCSV";
  
  	Для Каждого Ст ИЗ Таблица.Колонки Цикл
  		Рек = Элементы.Добавить("Колонка" + Ст.Имя, Тип("ПолеФормы"), Таб);
  		Рек.Вид = ВидПоляФормы.ПолеНадписи;
  		Рек.ПутьКДанным = "ТаблицаCSV" + "." + Ст.Имя;
  		Рек.Заголовок = Ст.Имя;
  	КонецЦикла;
  	
  	
  	ЗначениеВРеквизитФормы(Таблица,"ТаблицаCSV");

Захотелось немного покуражиться и программно добавить таблицу значений на форму. Хотя у этого была и рациональная причина — в дальнейшем не нужно будет каждый раз переделывать код, если файл с данными будет с другим количеством и названием колонок.

Итак, мы прочитали данные, переходим к следующему шагу.

Шаг 2. Загрузить данные в непосредственно в базу

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

А также команду "ЗагрузитьВБазу" и к ней соответствующие процедуры "ЗагрузитьВБазуНаКлиенте" и "ЗагрузитьВБазуНаСервере".

Рассмотрим код процедуры "ЗагрузитьВБазуНаСервере()".

Он также состоит из нескольких частей. Первая часть кода:

  Процедура ЗагрузитьВБазуНаСервере()
  
  	ТаблицаСЫМ=РеквизитФормыВЗначение("ТаблицаCSV");
  	
  	МенеджерВТ=Новый МенеджерВременныхТаблиц; 
  	
  	Запрос = Новый Запрос;
  	Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
  	
  	Запрос.Текст = 
  		"ВЫБРАТЬ
  		|	ВнешняяТабл1.date КАК Дата,
  		|	ВнешняяТабл1.account КАК Счет,
  		|	ВнешняяТабл1.category КАК Статья,
  		|	ВнешняяТабл1.amount КАК Сумма,
  		|	ВнешняяТабл1.currency КАК Валюта
  		|ПОМЕСТИТЬ ВнешняяТаблица
  		|ИЗ
  		|	&ВнешняяТабл1 КАК ВнешняяТабл1";
  	
  	Запрос.УстановитьПараметр("ВнешняяТабл1",ТаблицаСЫМ);
  	РезультатЗапроса = Запрос.Выполнить();
  

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

Вторая часть кода

  Запрос2 = Новый Запрос;
  	Запрос2.МенеджерВременныхТаблиц = МенеджерВТ;
  
  	Запрос2.Текст = 
  		"ВЫБРАТЬ
  		|	ВнешняяТаблица.Дата КАК Дата,
  		|	ВнешняяТаблица.Счет КАК Счет,
  		|	ВнешняяТаблица.Статья КАК Статья,
  		|	ВнешняяТаблица.Сумма КАК Сумма,
  		|	ВнешняяТаблица.Валюта КАК Валюта
  		|ИЗ
  		|	ВнешняяТаблица КАК ВнешняяТаблица";
  	
  	Запрос2.УстановитьПараметр("ЧленСемьи", ЧленСемьи);
  	
  	
  	РезультатЗапроса2 = Запрос2.Выполнить();
  	
  	ВыборкаДетальныеЗаписи = РезультатЗапроса2.Выгрузить();
  		
  	Для Каждого Строка Из ВыборкаДетальныеЗаписи Цикл
  	СтркСум=СтрЗаменить(Строка.Сумма," ","");
  	СтркСС=СтрЗаменить(СтркСум, ".",",");
  	
  		
  		Если Число(СтркСС) > 0 Тогда
  	            
  		Дох=Документы.ДобавитьДоход.СоздатьДокумент();
  		Дох.Дата=ДатаИзСтроки(Строка.Дата);
  		Дох.Счет=ПолучитьСсылкуСтатья(Справочники.Счета, Строка.Счет) ;
  		Дох.СтатьяДоходов=ПолучитьСсылкуСтатья(Справочники.СтатьиДоходов, Строка.Статья);
  		Дох.ФИО=ЧленСемьи;          
  		ДохТаб=Дох.ТабличнаяЧасть1.Добавить();
  		ДохТаб.Сумма=Число(СтркСС);
  		ДохТаб.Валюта=ПолучитьСсылкуСтатья(Справочники.Валюты, Строка.Валюта);
  		Дох.Записать(РежимЗаписиДокумента.Запись);
  
  				
  	ИначеЕсли  Число(СтркСС) < 0 Тогда
  
  		
  		Расх=Документы.ДобавитьРасход.СоздатьДокумент();
  		Расх.Дата=ДатаИзСтроки(Строка.Дата);
  		Расх.Счет=ПолучитьСсылкуСтатья(Справочники.Счета, Строка.Счет);
  		Расх.СтатьяЗатрат=ПолучитьСсылкуСтатья(Справочники.СтатьиЗатрат, Строка.Статья);
  		Расх.ФИО=ЧленСемьи;
  		Сум=Число(СтркСС);
  		РасхТаб=Расх.ТабличнаяЧасть1.Добавить();
  		РасхТаб.Сумма=(0-Сум);
  		РасхТаб.Валюта=ПолучитьСсылкуСтатья(Справочники.Валюты, Строка.Валюта);
  		Расх.Записать(РежимЗаписиДокумента.Запись);
                          
  	
  		
  	КонецЕсли;
  	
  			
  	КонецЦикла;

Здесь мы уже непосредственно загружаем прочитанные данные в базу (конечно, второй запрос можно было не делать, но мне так показалось удобнее).

Рассмотрим некоторые строки кода детальнее.

  Для Каждого Строка Из ВыборкаДетальныеЗаписи Цикл
  	СтркСум=СтрЗаменить(Строка.Сумма," ","");
  	СтркСС=СтрЗаменить(СтркСум, ".",",");

Так как, по сути, мы читали текстовый файл, то строки с суммами пришлось обработать — убрать лишние пробелы и заменить "." на ","

  Если Число(СтркСС) > 0 Тогда

Данные, которые мы загружаем, имеют простую структуру: суммы + доходы суммы — расходы. Поэтому простое условие >0 позволило нам разделить данные для прихода и расхода.

  Дох=Документы.ДобавитьДоход.СоздатьДокумент();

 Программно создаем документ, куда будем заносить данные

  Дох.Дата=ДатаИзСтроки(Строка.Дата);
  

Здесь пришлось повозиться — до меня не сразу дошло, что если программно создаешь документ, то и дату туда надо заносить в формате "год.месяц.день час:мин:сек" пришлось добавить функцию для даты

  Функция ДатаИзСтроки(стрДата) экспорт // "01.12.2011" преобразует в '01.12.2011 0:00:00'
  	Попытка
  		возврат Дата(Сред(стрДата,7,4)+Сред(стрДата,4,2)+Лев(стрДата,2))
  	Исключение
  		возврат '00010101'
  	КонецПопытки;
  КонецФункции

 Сразу скажу — спешил, поэтому функцию нашел здесь же на форуме. Сейчас пишу и думаю, что можно было сделать то же самое, используя "СтрРазделить", но код был бы длиннее.

  Дох.Счет=ПолучитьСсылкуСтатья(Справочники.Счета, Строка.Счет);

База, по сути, — пустая, и мне не хотелось вручную наполнять справочники, поэтому добавил функцию:

  &НаСервере
  Функция ПолучитьСсылкуСтатья(СправочникСтатья, СтатьяНаименование)
  	Перем СтатьяСсылка;
  	
  	
  	СтатьяСсылка = СправочникСтатья.НайтиПоНаименованию(СтатьяНаименование);
  	Если СтатьяСсылка = СправочникСтатья.ПустаяСсылка() Тогда
  		СтатьяНовая = СправочникСтатья.СоздатьЭлемент();
  		СтатьяНовая.Наименование = СтатьяНаименование ;
  		СтатьяНовая.Записать();
  		СтатьяСсылка = СтатьяНовая.Ссылка;
  	КонецЕсли;
  	      
  	Возврат СтатьяСсылка;
  КонецФункции
  

Так как справочников много, сделал два параметра "СправочникСтатья" и "СтатьяНаименование". 

Вроде основное пояснил.

Всем Спасибо. 

has been added to your cart:
Оформление заказа