При интеграции 1С со сторонними сайтами и веб-сервисами программисты часто сталкиваются с проблемой искажения кириллицы («кракозябрами»). Рассмотрим типичную ситуацию: вы отправляете HTTP-запрос, получаете ответ в формате XML и пытаетесь прочитать его с помощью объекта ЧтениеXML, передавая текст через метод УстановитьСтроку. Однако при обходе узлов текстовые поля содержат некорректные символы.
Проанализируем причину такого поведения и разберем по шагам несколько эффективных способов решения этой задачи на основе обсуждений опытных разработчиков и особенностей платформы 1С. Если же работа через запросы невозможна, можно использовать программных роботов для автоматизации действий на сайте.
Для начала разберемся, почему возникает сбой кодировки. Внутренние строки в 1С всегда хранятся в формате UTF-16. Метод УстановитьСтроку объекта ЧтениеXML не принимает параметров кодировки именно потому, что он ожидает уже корректно сформированную строку.
Проблема искажения символов возникает на этапе превращения сырого ответа от сайта в строку 1С. Если HTTP-ответ по умолчанию считывается платформой как UTF-8, а реальные данные пришли в кодировке windows-1251, то кириллица "бьется" еще до того, как попадет в парсер XML. То есть у вас в переменной УЖЕ находится строка в неправильной кодировке.
Как справедливо заметили разработчики, получать строку в нужной кодировке необходимо еще в момент извлечения данных из объекта HTTPОтвет. Для этого у метода извлечения тела есть специальный параметр.
Посмотрим на пример правильного получения тела запроса: для регулярного сбора и выгрузки файлов через интернет подойдет программа для автоматической обработки данных по HTTP.
// Получаем ответ от сервера
HttpОтвет = Соединение.Получить(Запрос);
// Сразу указываем правильную кодировку при конвертации в строку
ТелоЗапроса = HttpОтвет.ПолучитьТелоКакСтроку("windows-1251");
// Теперь строка корректна, можем передавать её в парсер
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(ТелоЗапроса);
Пока ЧтениеXML.Прочитать() Цикл
// Обработка узлов
КонецЦикла;
Этот способ устраняет саму причину искажения и является наиболее предпочтительным. Для интерактивной проверки ответов сервера и отладки параметров запроса можно использовать консоль HTTP-запросов для управляемых форм.
Если по какой-то причине вы не можете изменить способ получения ответа на начальном этапе (например, строка передается вам из закрытого модуля), можно попробовать перекодировать уже полученную искаженную строку через двоичные данные. Посмотрим на пример кода:
// Пытаемся восстановить исходные байты и прочитать их заново
ТелоЗапроса = ПолучитьСтрокуИзДвоичныхДанных(
ПолучитьДвоичныеДанныеИзСтроки(ИскаженнаяСтрока, КодировкаТекста.UTF8, Ложь),
КодировкаТекста.ANSI
);
Важно: Этот метод срабатывает не всегда (что подтвердил автор исходного вопроса), так как при первичном некорректном чтении некоторые байты могут быть безвозвратно утеряны или заменены на символы замены UTF. Применяйте этот подход только в крайних случаях.
Вместо получения ответа в виде строки, современные стандарты разработки в 1С рекомендуют использовать потоки. Это позволяет избежать лишних преобразований строк в оперативной памяти и перекладывает работу с кодировками на саму платформу. Примером такой реализации является обработка для загрузки данных о продажах Wildberries по API.
Рассмотрим, как это реализовать:
// Получаем ответ в виде потока
ПотокОтвета = HttpОтвет.ПолучитьТелоКакПоток();
ЧтениеXML = Новый ЧтениеXML;
// Парсер сам прочитает кодировку из XML-заголовка
ЧтениеXML.ОткрытьПоток(ПотокОтвета);
Пока ЧтениеXML.Прочитать() Цикл
// Ваша логика парсинга
КонецЦикла;
ЧтениеXML.Закрыть();
При использовании ОткрытьПоток парсер ЧтениеXML анализирует первую строку документа — XML-декларацию. Если там указано <?xml version="1.0" encoding="windows-1251"?>, платформа автоматически применит правильную кодировку.
Иногда возникает ситуация, когда фактическая кодировка файла — windows-1251, но в заголовке XML (декларации) по ошибке со стороны стороннего сайта отдается encoding="UTF-8" или заголовок вовсе отсутствует. В таком случае парсер 1С выдаст ошибку (например, «Неожиданный символ») или прочитает текст некорректно.
Решением станет принудительная замена текста в заголовке. Проанализируем этот шаг:
// Ищем ошибочный заголовок и заменяем его на корректный
ТелоЗапроса = СтрЗаменить(ТелоЗапроса, "encoding=""UTF-8""", "encoding=""windows-1251""");
Такая манипуляция синхронизирует фактическую кодировку текста с тем, что ожидает встроенный парсер 1С.
Если данные с сайта сохраняются во временный файл, у нас появляется еще больше гибкости. Объект ЧтениеXML имеет метод ОткрытьФайл, который позволяет жестко задать кодировку чтения:
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ИмяВременногоФайла, "windows-1251");
Также для сложных случаев конвертации можно использовать промежуточный объект ЧтениеТекста, который отлично справляется с перекодировками перед передачей данных в XML-парсер:
// Читаем файл как простой текст в нужной кодировке
ЧтениеТекста = Новый ЧтениеТекста(ПутьКФайлу, "windows-1251");
КорректнаяСтрока = ЧтениеТекста.Прочитать();
ЧтениеТекста.Закрыть();
// Передаем исправленную строку в парсер
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(КорректнаяСтрока);
В завершение разберем альтернативный подход к обработке XML-документов. Помимо последовательного чтения через цикл (Пока ЧтениеXML.Прочитать()), вы можете использовать объект ПостроительDOM. Он принимает на вход предварительно настроенный объект ЧтениеXML (с уже решенной проблемой кодировки одним из способов выше) и выстраивает весь документ в виде дерева.
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(КорректнаяСтрока);
Построитель = Новый ПостроительDOM;
ДокументDOM = Построитель.Прочитать(ЧтениеXML);
// Далее работаем с объектом ДокументDOM для поиска узлов
Элементы = ДокументDOM.ПолучитьЭлементыПоИмени("MyNode");
Подведем итог: При работе с кириллицей и кодировкой windows-1251 из HTTP-ответов всегда старайтесь задавать кодировку на самом первом этапе — при получении тела запроса (ПолучитьТелоКакСтроку("windows-1251")) или используйте чтение через потоки (ПолучитьТелоКакПоток()). Это убережет ваши данные от необратимых искажений в строковых переменных платформы 1С.