При разработке механизмов обмена данными, особенно при загрузке банковских выписок или электронных чеков, программисты часто сталкиваются с ситуацией, когда вместо понятного русского текста в информационную базу попадает странный набор символов — так называемая «абракадабра» (модзибакэ). Рассмотрим типичный пример: 711Рќ6394001;0100008)РГК. На первый взгляд это кажется нечитаемым набором байтов, но для опытного разработчика это четкий сигнал о проблеме интерпретации кодировок.
В данной статье мы подробно разберем, почему возникает такая ситуация, проанализируем механизмы работы платформы 1С с кодировками и реализуем надежное решение, которое позволит вашей обработке корректно читать файлы в любом формате.
Давайте проанализируем ситуацию. Текст вида Рќ вместо русских букв — это классический признак того, что файл, сохраненный в кодировке UTF-8, пытаются прочитать как Windows-1251 (стандартная кириллическая кодировка в Windows). В UTF-8 каждый символ кириллицы кодируется двумя байтами, и первый байт большинства русских букв (с кодом D0 или D1) в однобайтовой кодировке Windows-1251 отображается как буквы «Р» или «С».
Проблема часто кроется в том, что объект ЧтениеXML не всегда может автоматически определить кодировку файла, если в самом начале XML-документа отсутствует заголовок (декларация) вида <?xml version="1.0" encoding="UTF-8"?>. Если этого тега нет или он содержит неверную информацию, 1С может попытаться прочитать файл в системной кодировке по умолчанию, что и приводит к искажению данных.
Многие начинающие разработчики пытаются использовать свойства ЧтениеXML.КодировкаXML или ЧтениеXML.КодировкаИсточника, надеясь «заставить» объект сменить кодировку на лету. Однако, если мы обратимся к документации, то увидим, что эти свойства доступны только для чтения. Они показывают результат того, как 1С уже распознала файл, но не позволяют программно изменить этот выбор после открытия файла.
Метод ОткрытьФайл() у объекта ЧтениеXML также не имеет параметра для явного указания кодировки. Поэтому нам необходимо подготовить данные заранее, прежде чем передавать их в парсер XML.
Рассмотрим наиболее эффективный способ, предложенный в обсуждении. Суть метода заключается в том, чтобы сначала прочитать содержимое файла как обычный текст с явным указанием кодировки КодировкаТекста.UTF8, а затем передать полученную строку в объект ЧтениеXML.
Разберем по шагам реализацию функции, которая получает данные из временного хранилища и подготавливает их для парсинга:
&НаСервере
Функция ЧтениеXMLИзДвоичныхДанных(АдресДвоичныхДанных)
// Создаем путь для временного файла на сервере
ВременныйФайл = ПолучитьИмяВременногоФайла("xml");
// Извлекаем двоичные данные из хранилища (куда они попали после выбора файла пользователем)
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресДвоичныхДанных);
// Записываем данные во временный файл для последующего текстового чтения
ДвоичныеДанные.Записать(ВременныйФайл);
// Используем объект ЧтениеТекста, где мы МОЖЕМ явно указать кодировку UTF8
ЧтениеТекста = Новый ЧтениеТекста;
ЧтениеТекста.Открыть(ВременныйФайл, КодировкаТекста.UTF8);
// Читаем весь файл целиком в одну строковую переменную
СодержимоеФайла = ЧтениеТекста.Прочитать();
ЧтениеТекста.Закрыть();
// Удаляем временный файл, так как данные уже в памяти
УдалитьФайлы(ВременныйФайл);
// Инициализируем ЧтениеXML и загружаем в него готовую строку
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(СодержимоеФайла);
Возврат ЧтениеXML;
КонецФункции
После этого мы можем работать с объектом ЧтениеXML привычным способом, используя цикл Пока ЧтениеXML.Прочитать(). Поскольку мы передали в него уже «расшифрованную» строку, проблема с кодировкой исчезнет. Для разбора полученных XML-данных и их конвертации в структуру 1С удобно использовать готовую функцию чтения XML в структуру.
Метод с чтением всего файла в строку через ЧтениеТекста.Прочитать() прост и надежен, но у него есть существенный минус: если файл банковской выписки будет очень большим (десятки или сотни мегабайт), оперативная память сервера может переполниться. В современных версиях платформы 1С (начиная с 8.3.10) реализована поддержка потоков, которая позволяет решить задачу более элегантно.
Проанализируем, как можно использовать ЧтениеТекста в качестве обертки напрямую, не загружая весь текст в память:
&НаСервере
Процедура ПрочитатьXMLЧерезПоток(АдресДвоичныхДанных)
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресДвоичныхДанных);
// Получаем поток из двоичных данных
Поток = ДвоичныеДанные.ОткрытьПотокДляЧтения();
// Создаем чтение текста на основе потока с указанием кодировки
ЧтениеТекста = Новый ЧтениеТекста(Поток, КодировкаТекста.UTF8);
// Инициализируем ЧтениеXML
ЧтениеXML = Новый ЧтениеXML;
// Открываем ЧтениеXML, передавая в него объект ЧтениеТекста
// Платформа будет сама подтягивать данные из потока частями
ЧтениеXML.ОткрытьФайл(ЧтениеТекста);
Пока ЧтениеXML.Прочитать() Цикл
// Обработка узлов XML
Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
Если ЧтениеXML.Имя = "PayPurpose" Тогда
// Здесь текст будет уже в правильной кодировке
НазначениеПлатежа = ЧтениеXML.ПрочитатьСодержимоеКакСтроку();
КонецЕсли;
КонецЕсли;
КонецЦикла;
ЧтениеXML.Закрыть();
ЧтениеТекста.Закрыть();
Поток.Закрыть();
КонецПроцедуры
Этот способ является наиболее профессиональным, так как он сочетает в себе и корректную обработку кодировок, и бережное отношение к ресурсам сервера.
Для того чтобы в будущем минимизировать ошибки при обмене данными, придерживайтесь следующих правил:
ЗаписьXML и метод УстановитьСтроку() или ОткрытьФайл() с указанием кодировки, чтобы на стороне получателя не возникало аналогичных проблем.& или < без экранирования. Объект ЧтениеXML выдаст ошибку, если встретит их. Если файл сформирован некорректно, вам придется сначала «почистить» строку методами СтрЗаменить().КодировкаТекста.UTF8 становится обязательным условием стабильной работы.Подводя итог, можно сказать, что «абракадабра» в 1С — это не приговор и не ошибка в данных, а лишь неверно выбранный «декодер». Использование объекта ЧтениеТекста в связке с ЧтениеXML позволяет полностью контролировать процесс преобразования байтов в понятные пользователю символы. Кроме того, при разработке надежных механизмов обмена полезно задействовать обработку для проверки валидности XML-файла на соответствие схеме XSD.