gifts2017

Запись текста в кодировке UTF-8 без BOM из 1с 8.2

Опубликовал Андрей Чибидин (andrewbc) в раздел Программирование - Практика программирования

Как-то хочется из 8.2 записать в кодировке "UTF-8 без BOM" любым из менее затратных способов.

Смысл такой: на WEB-сайт потребовалось выгрузить файл в кодировке UTF-8. Но проблема в том, что UTF-8 имеет 2 варианта: "с BOM" и "без BOM". Файлы, сохраненные в этих двух кодировках, получаются одинаковые за исключением того, что в первом случае в начало файла добавлены три байта EF BB BF (в 16-ричной системе счисления). Это и есть символы BOM (сигнатура Byte Order Mark). Так вот, на сайт нужно было отправить "без BOM", а 1с 8.2 сохраняет исключительно "с BOM".

Вот из-за того, что 1с 8.2 не может сохранять текстовые файлы в нужном мне формате и возникла идея "вырезать" эти символы из двоичного файла, но как это сделать в 1с 8.2, я не нашел. Там вообще работа с двоичными данными очень ограничена.

Поэтому пошел по простому пути: что не изобретено, на том катаемся.
Придумал через "очень много" так:


это приблизительный код вызова:


текст = Новый ТекстовыйДокумент;
...
здесь в "текст" что-то пихаем
...
папкаФайла = "какая-то папка\";
папкаВОМ = папкаФайла+"temp_BOM\";
имяФайла = "как-то.почему-то";
СоздатьКаталог(папкаВОМ);
текст.Записать(папкаВОМ+имяФайла, КодировкаТекста.UTF8, Символы.ВК);
УбитьВОМ(папкаВОМ+имяФайла, папкаФайла+имяФайла, папкаВОМ);


далее непосредственно главная процедура "УбитьВОМ":


Процедура УбитьВОМ(Знач ИсходныйФайл, РезультирующийФайл, ВременнаяПапка, МассивФайлов = Неопределено)
    Если МассивФайлов = Неопределено Тогда
        МассивФайлов = Новый Массив;
    КонецЕсли;
    бин = Новый ДвоичныеДанные(ИсходныйФайл);
    размер = бин.Размер();
    новыйРазмер = Макс(Окр(размер/2,0),3);
    массив = РазделитьФайл(ИсходныйФайл,новыйРазмер);
    Если массив.Количество() = 2 Тогда
        МассивФайлов.Вставить(0,массив[1]);
    КонецЕсли;
    Если новыйРазмер = 3 Тогда
        ОбъединитьФайлы(МассивФайлов,РезультирующийФайл);
        УдалитьФайлы(ВременнаяПапка);
    Иначе
        УбитьВОМ(массив[0],РезультирующийФайл,ВременнаяПапка,МассивФайлов);
    КонецЕсли;
КонецПроцедуры


В результате получается файл в кодировке "UTF-8 без BOM".

Примечание. понимаю, что через "ж", но если кто подскажет другой вариант это будет интересно со всех сторон.

А в остальном: кому нужно - пользуйтесь и экспериментируйте.

См. также

Подписаться Добавить вознаграждение

Комментарии

1. andrewks 26.05.12 15:35
если в виндах, то можно заюзать ADODB.Stream

например, так:

		Попытка
			Файл=СоздатьОбъект("ADODB.Stream");
			Файл.Mode=3; // r/w
			Файл.Type=1; //1-Binary, 2-Text
			Файл.Open();
			Файл.LoadFromFile(ИмяФайлаДанных);
			Файл.Position=3;
			ТекстБезБОМ=СоздатьОбъект("ADODB.Stream");
			ТекстБезБОМ.Mode=3; // r/w
			ТекстБезБОМ.Type=1; //1-Binary, 2-Text
			ТекстБезБОМ.Open();
			Файл.CopyTo(ТекстБезБОМ);
			Файл.Close();
			ТекстБезБОМ.SaveToFile(ИмяФайлаДанных,2);
			ТекстБезБОМ.Close();
		Исключение
			ТекстОшибки=ОписаниеОшибки();
			Сообщить(ТекстОшибки,"!");
			Возврат;
		КонецПопытки;

...Показать Скрыть


синтаксис в примере 7-шный, но код применим для любой версии - 7.7, 8.х, главное, чтобы доступ к объекту ADODB.Stream был
adhocprog; andrewbc; +2 Ответить 2
2. Serj (Serj1C) 28.05.12 12:12
Еще можно провернуть такой танец с бубном (без ВК, работа в памяти, без файлов):
1) получить двоичные данные из файла (как у вас)
2) преобразуем двоичные данные в строку с помощью ЗначениеИзСтрокиВнутр
3) анализируем формат, там не сложно, удаляем лишние переносы кареток (символ 10 вроде) // при желании могу описать подробнее
4) упаковываем поток обратно, получая двоичные данные
5) записываем в файл. Профит
3. Андрей Чибидин (andrewbc) 29.05.12 10:41
(1) andrewks, отлично отработало в следующем виде:

Процедура УбитьВОМ(ИсходныйФайл,РезультирующийФайл)
Попытка
файл = Новый ComObject("ADODB.Stream");
файл.Mode = 3; // r/w
файл.Type = 1; //1-Binary, 2-Text
файл.Open();
файл.LoadFromFile(ИсходныйФайл);
файл.Position = 3;
текстБезБОМ = Новый ComObject("ADODB.Stream");
текстБезБОМ.Mode = 3; // r/w
текстБезБОМ.Type = 1; //1-Binary, 2-Text
текстБезБОМ.Open();
файл.CopyTo(текстБезБОМ);
файл.Close();
текстБезБОМ.SaveToFile(РезультирующийФайл,2);
текстБезБОМ.Close();
УдалитьФайлы(ИсходныйФайл);
Исключение
Сообщить(ОписаниеОшибки(),СтатусСообщения.Важное);
КонецПопытки;
КонецПроцедуры
Mixailovna; +1 Ответить
4. Андрей Чибидин (andrewbc) 29.05.12 11:09
(2) Serj1C, не получилось сотворить такой вариант обработки:

1. при преобразовании "ЗначениеВСтрокуВнутр" 1с преобразует двоичные данные в формат base64, при этом длина строки становится больше размера иходного файла почти в полтора раза, это значит, что у нас уже текст не в кодировке UTF-8.
2. не совсем понятно: символы перевода строки (10) (возврат каретки - 13) обязательно удалять? а если это UTF-ный символ с кодом 10?
3. каким образом "упаковать поток обратно"? у объекта "ДвоичныеДанные" только 2 метода - "Размер()" и "Записать(ИмяФайла").
4. как записать в файл результат?

пробовал еще такой вариант:

бин = Новый ДвоичныеДанные(ИсходныйФайл);
бинСтрока = Base64Строка(бин);
новаяБинСтрока = Сред(бинСтрока,4);
бин = Base64Значение(новаяБинСтрока);
бин.Записать(РезультирующийФайл);

но там вообще ерунда получается.
может, примерчик рабочий есть?
5. Serj (Serj1C) 29.05.12 12:23
(4) andrewbc, оформлять как обработку не буду, функции просто положу:

Функция Текст2Base64(Текст, HEX=Ложь)  Экспорт
	// Описание: http://ru.wikipedia.org/wiki/Base64
	Множитель1 = 256;
	Множитель2 = 64;
	СловарьСтрока = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456­789+/=";
	Словарь = Новый Соответствие;
	Для Сч=0 по 64 Цикл
		Словарь.Вставить(Сч, Сред(СловарьСтрока, Сч+1, 1));
	КонецЦикла;
	
	Если HEX=Истина Тогда
		К = 6;
	Иначе
		К = 3;
	КонецЕсли;
	
	ДлинаТекста = СтрДлина(Текст);
	КоличествоЧастей = Цел(ДлинаТекста / К) + ?(ДлинаТекста%К=0, 0, 1);
	Результат = "";
	Для НомерЧасти = 1 По КоличествоЧастей Цикл
		Часть = Сред(Текст, К*(НомерЧасти-1)+1, К);
		
		Если HEX Тогда
			Часть1 = ш(Сред(Часть, 1, 1));
			Часть1 = ш(Сред(Часть, 2, 1)) + Часть1*16;
			Часть2 = ш(Сред(Часть, 3, 1));
			Часть2 = ш(Сред(Часть, 4, 1)) + Часть2*16;
			Часть3 = ш(Сред(Часть, 5, 1));
			Часть3 = ш(Сред(Часть, 6, 1)) + Часть3*16;
		Иначе
			Часть1 = КодСимвола(Сред(Часть, 1, 1));
			Часть2 = КодСимвола(Сред(Часть, 2, 1));
			Часть3 = КодСимвола(Сред(Часть, 3, 1));
		КонецЕсли;
		
		БольшоеЧисло = Часть1 * Множитель1 * Множитель1 + ?(Часть2=-1, 0, Часть2) * Множитель1 + ?(Часть3=-1, 0, Часть3);
		
		Часть4_ = БольшоеЧисло % Множитель2;
		БольшоеЧисло = (БольшоеЧисло-Часть4_) / Множитель2;
		Часть3_ = БольшоеЧисло % Множитель2;
		БольшоеЧисло = (БольшоеЧисло-Часть3_) / Множитель2;
		Часть2_ = БольшоеЧисло % Множитель2;
		БольшоеЧисло = (БольшоеЧисло-Часть2_) / Множитель2;
		Часть1_ = БольшоеЧисло;
		
		Если СтрДлина(Часть)=1*?(HEX, 2, 1)  Тогда
			Часть3_ = 64;
			Часть4_ = 64;
		ИначеЕсли СтрДлина(Часть)=2*?(HEX, 2, 1) Тогда
			Часть4_ = 64;
		КонецЕсли;
		
		Результат = Результат + Словарь.Получить(Часть1_) + Словарь.Получить(Часть2_) + Словарь.Получить(Часть3_) + Словарь.Получить(Часть4_);
	КонецЦикла;
	
	Возврат Результат;
КонецФункции

Функция Ш(С) Экспорт
	Возврат ?(С>="0" и С<="9", КодСимвола(С)-КодСимвола("0"),
		?(С>="A" и С<="F", КодСимвола(С)-КодСимвола("A")+10, -1));
КонецФункции

Процедура Text2Base64(Текст64)
	
	з = "{""#"",87126200-3e98-44e0-b931-ccb1d7edc497,
		|{1,
		|{#base64:"+Текст64+"}
		|}
		|}";
	д = ЗначениеИзСтрокиВнутр(з);
	Сообщить(д);
	д.Записать("e:\1.f");
	
КонецПроцедуры

Процедура Base642Text(Текст64)
	ф = "e:\2.txt";
	норм = "{""#"",87126200-3e98-44e0-b931-ccb1d7edc497,
		|{1,
		|{#base64:"+Текст2Base64(Текст64, Истина)+"}
		|}
		|}";
	д = ЗначениеИзСтрокиВнутр(норм);
	д.Записать(ф);
	Сообщить(д);
КонецПроцедуры
...Показать Скрыть
6. mc2 30.05.12 10:37
Тоже долго мучился пока нашел как это сделать для записи текстовых файлов средствами 1С:

УдалитьФайлы(ИмяФайла); // надо убедиться, что файл не существует, т.к. если он существует, то данные добавятся в конец файла

ЗТ = Новый ЗаписьТекста(ИмяФайла,,, Истина, Символы.ПС);
ЗТ.Записать(МояСтрока);
ЗТ.Закрыть();

И никаких извращений...
Lapitskiy; adhocprog; davdykin; lutsiy2; leozx; New Look; ErrorEd88; 1st RUS; vlad.frost; andrewbc; +10 Ответить 4
7. Андрей Чибидин (andrewbc) 31.05.12 12:11
(6) mc2, шикарный вариант, именно то, что нужно. спасибо!
8. Андрей Чибидин (andrewbc) 31.05.12 12:14
(5) Serj1C, наворочено как-то. для записи простого файлика в формате csv слишком много телодвижений.
самый простой и подходящий вариант - (6).
хотя для общего развития полезно. спасибо.
9. Serj (Serj1C) 31.05.12 12:50
(9) тут http://infostart.ru/public/137969/ тоже неплохо описан процесс записи произвольных данных в файл
10. Андрей Чибидин (andrewbc) 31.05.12 13:44
11. mc2 04.07.12 03:38
(7) andrewbc, Ну вот, в 8.3.1 без режима совместимости с 8.2.16, этот вариант работать больше не будет! Разработчики вместо того, чтобы делать то,что надо, делают то что не надо! Теперь извращения принимаются.
12. Михаил Журавлев (Gmix) 04.07.12 09:28
(4) andrewbc, Собственно так и делаю.
Только у вас ошибка.
Вот рабочий код:
файлЛог=ПолучитьИмяВременногоФайла(".txt");
	Текст.Записать(файлЛог);
	
	// теперь нужно получить файл без BOM
	ОБ_ДД=Новый ДвоичныеДанные(файлЛог);
	Стр_Base64=Base64Строка(ОБ_ДД);
	ОБ_ДД=Base64Значение(Сред(Стр_Base64,5));
	ОБ_ДД.Записать(файлЛог);
...Показать Скрыть


Использовал в Визуализация журнала регистрации Gource
В 8.3 не проверял еще, но думаю должно сработать.
13. mc2 04.07.12 14:50
(7) andrewbc, нашел новый способ, который годится и для 8.3:

ЗТ = Новый ЗаписьТекста(ИмяФайла, КодировкаТекста.ANSI);
ЗТ.Закрыть();
ЗТ = Новый ЗаписьТекста(ИмяФайла,,, Истина, Символы.ПС);
ЗТ.Записать(Данные);
ЗТ.Закрыть();
krv2k; davdykin; dour-dead; BigB; ErrorEd88; andrewbc; +6 Ответить 2
14. Андрей Чибидин (andrewbc) 04.07.12 15:18
(13) mc2, долго смеялся. Действительно, извращения принимаются. Как так удается находить таких блох? За терпение и находчивость - однозначный +.
15. Андрей Чибидин (andrewbc) 04.07.12 15:20
(12) Gmix, этот вариант попробовал - работает. Спасибо за пример. Я просто в цифирьке ошибся. Бывает.
16. Alex Filippov (1st RUS) 07.11.12 18:25
(6) mc2,

Попробовал твой метод, и выяснил, что за вредные символы отвечает параметр "Дописывать" в конструкторе "ЗаписьТекста"

в общем получилось так:

УдалитьФайлы(имяФайлаОтправки); это да, это обязательно
ЗаписьТекста = Новый ЗаписьТекста(имяФайлаОтправки,КодировкаТекста.UTF8,,Истина,Символы.ПС);
ЗаписьТекста.ЗаписатьСтроку("--"+boundary);
ЗаписьТекста.ЗаписатьСтроку("Content-Disposition: form-data; name=""file""; filename=""goods.xml""");
ЗаписьТекста.ЗаписатьСтроку("Content-Type: text/xml");
ЗаписьТекста.ЗаписатьСтроку("");
и ниже пишем содержимое XML файла

СПАСИБО
sergsqr; xzorkiix; Жолтокнижниг; +3 Ответить 2
18. mc2 08.11.12 00:17
(16) 1st RUS, Посмотрите пожалуйста (13) пост этой темы, т.к. то, что было написано в (6) не работает в 8.3.
19. Алексей (Alex1c) 10.10.13 16:09
Писал обработку для хеширования по ГОСТ с помощью стороннего EXE. Программка работала через командную строку. Необходимо было указать ей путь к файлу с данными на вход. Она генерила выходной файл с хешем.
Поимел проблему. У меня был тестовый файл и файл сгенеренный 1С, с виду абсолютно одинаковые. Разница в размере в 2 байта. Хеш программа выдавала разный. Оказалось что по умолчанию 1С указывается символ перевода строки и при создании файла и при добавлении строки. Указал в обоих местах в качестве символа "" и все пошло. Использовал много идей из этого поста. Возможно кому то будет полезен полный код.

Функция ПолучитьХЭШГОСТ(СтрокаНаВход)


// очистили входящий файл
УдалитьФайлы(КаталогВременныхФайлов()+"in.txt");
ТекстIn = Новый ЗаписьТекста(КаталогВременныхФайлов()+"in.txt", КодировкаТекста.UTF8,"", Ложь,);
ТекстIn.ЗаписатьСтроку(Строка(СокрЛП(ВРЕГ(ВходящиеДанные))));
ТекстIn.Закрыть();

// обрезаем ВОМ
ОБ_ДД=Новый ДвоичныеДанные(КаталогВременныхФайлов()+"in.txt");
Стр_Base64=Base64Строка(ОБ_ДД);
ОБ_ДД=Base64Значение(Сред(Стр_Base64,5));
ОБ_ДД.Записать(КаталогВременныхФайлов()+"in.txt");

// очистили выходной файл
УдалитьФайлы(КаталогВременныхФайлов()+"out.txt");
ТекстOut = Новый ЗаписьТекста(КаталогВременныхФайлов()+"out.txt", КодировкаТекста.UTF8,,Ложь,);
ТекстOut.Закрыть();

//запустили приложение хеширования
ЗапуститьПриложение(Строка(ИмяФайла)+" --gost-cryptopro "+КаталогВременныхФайлов()+"in.txt"+" "+"--output="+КаталогВременныхФайлов()+"out.txt",,Истина,);

//прочитали из выходного файла результат
Текст = Новый ЧтениеТекста(КаталогВременныхФайлов()+"out.txt", КодировкаТекста.UTF8);
Стр = Текст.ПрочитатьСтроку(""); //при создании файла перевод строки убрали,при чтении тоже убрали, то есть читаем весь файл как строку

Стр = СтрЗаменить(Стр,Строка(КаталогВременныхФайлов()+"in.txt"),"");

РезультатХеширования = Врег(Стр);

Возврат РезультатХеширования;

КонецФункции
20. Эдуард Неженцев (ErrorEd88) 24.01.14 22:29
Кодировка UTF-8 без BOM называется "CESU-8". Подсказали.
mc2; malikov_pro; Никс; h00k; rysha; stvorl; 1v7; Valet; sergsqr; AndroidK; DitriX; inhuman; Enot; krv2k; dmitryafanasyev91; DrBlack; Широкий; PolAlex2; Aleksey.Bochkov; BigB; adhocprog; deaddy64; mc_bublik_90@mail.ru; ilya4; +24 Ответить 5
21. andrewks 25.01.14 07:25
(20) ErrorEd88, да ну! и где же об этом написано в описании стандарта? http://www.unicode.org/reports/tr26/
22. Евгений Шабалин (xzorkiix) 04.06.14 11:41
(16) 1st RUS, спасибо за

ЗаписьТекста = Новый ЗаписьТекста(имяФайлаОтправки,КодировкаТекста.UTF8,,Истина,Символы.ПС); 


то что надо!
23. Дмитрий Неважно (mc_bublik_90@mail.ru) 01.10.14 05:01
24. Evgeniy Nikolaenko (DrBlack) 27.08.15 12:33
(20) ErrorEd88, Спасибо!
Информация верная!!! :)
25. Дмитрий Афанасьев (dmitryafanasyev91) 13.10.15 11:48
26. Дмитрий Смоляков (loky12) 10.08.16 12:17
(20) ErrorEd88,
Спасибо помог, очень выручило при склейке csv созданных в фоновых в многопоточной обработке.
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа