При получении текстовых данных с веб-сайтов через HTTP-запросы разработчики часто сталкиваются с тем, что вместо привычного русского текста в 1С загружаются либо знаки вопроса ???, либо нечитаемые символы (так называемый «модзибакэ» или «кракозябры») вида Ii?ou/ia?aoiau или Ïîðò. Особенно остро эта проблема проявляется при работе со старыми ресурсами, использующими кодировку Windows-1251 (CP1251).
В этой статье мы подробно разберем, почему возникает эта ошибка, проанализируем механизмы работы платформы 1С с различными кодировками и рассмотрим несколько надежных способов решения проблемы: от современных кроссплатформенных методов до обходных путей для устаревших систем.
Основная причина появления символов вроде Ii?ou/ia?aoiau заключается в неверном декодировании полученного байтового потока. Когда мы используем COM-объект WinHttp.WinHttpRequest.5.1 и обращаемся к его свойству ResponseText, библиотека пытается самостоятельно преобразовать бинарное тело ответа в строку.
Если веб-сервер в HTTP-заголовке ответа Content-Type не указывает кодировку явно (например, отсутствует параметр charset=windows-1251), библиотека WinHttpRequest декодирует данные по умолчанию в кодировку ISO-8859-1 (Latin-1). Происходит следующее:
0xCF.0xCF по таблице ISO-8859-1 и превращает его в латинскую букву «Ï».0xEE, который в ISO-8859-1 превращается в «î».В результате весь русский текст превращается в смесь латинских букв с умлаутами и спецсимволов. Обычные попытки перекодировать уже испорченную строку средствами 1С не принесут результата, так как исходная информация о байтах уже частично утеряна. Нам необходимо работать не с искаженным текстом, а непосредственно с «сырыми» двоичными данными (байтами).
---
Если вы работаете на платформе 1С:Предприятие 8.3, забудьте об использовании нестабильных и платформозависимых COM-объектов. В платформе есть мощные встроенные инструменты для выполнения HTTP-запросов, которые работают как под Windows, так и под Linux, а также не зависят от разрядности клиента или сервера (32/64-бит).
Рассмотрим пример использования объектов HTTPСоединение и HTTPОтвет с явным указанием кодировки при получении тела запроса:
// Выделяем имя сервера и путь к ресурсу из общего адреса
ИмяСервера = "example.com";
ПутьКРесурсу = "/page_win1251.html";
// Создаем HTTP-соединение
Соединение = Новый HTTPСоединение(ИмяСервера);
Запрос = Новый HTTPЗапрос(ПутьКРесурсу);
Попытка
Ответ = Соединение.Получить(Запрос);
Если Ответ.КодСостояния = 200 Тогда
// Самый важный момент: принудительно указываем кодировку для декодирования байтов
ТекстСтраницы = Ответ.ПолучитьТелоКакСтроку("windows-1251");
// Теперь переменная ТекстСтраницы содержит корректный русский текст
Сообщить(Лев(ТекстСтраницы, 150));
Иначе
Сообщить("Ошибка сервера: " + Ответ.КодСостояния);
КонецЕсли;
Исключение
Сообщить("Ошибка подключения: " + ОписаниеОшибки());
КонецПопытки;
Если вам требуется сначала получить двоичные данные ответа, а затем преобразовать их в строку в определенном месте кода, используйте встроенную функцию ПолучитьСтрокуИзДвоичныхДанных():
ДвоичныеДанныеТела = Ответ.ПолучитьТелоКакДвоичныеДанные();
// Преобразуем бинарный поток в нормальную кириллицу
ТекстСтраницы = ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанныеТела, "windows-1251");
---
Если ваша конфигурация работает на старых версиях платформы (или даже на 1С 7.7) и вы вынуждены использовать WinHttpRequest, вы можете обойти проблему искажения символов. Для этого нельзя обращаться к WinHttp.ResponseText.
Вместо этого мы берем свойство ResponseBody (которое возвращает исходный массив байтов) и перекодируем его с помощью COM-объекта ADODB.Stream прямо в оперативной памяти, избегая медленных операций записи на жесткий диск.
Проанализируем этот алгоритм:
URL = "http://example.com/page_win1251.html";
WinHttp = Новый COMОбъект("WinHttp.WinHttpRequest.5.1");
WinHttp.Open("GET", URL, Ложь);
WinHttp.Send();
Если WinHttp.Status = 200 Тогда
// Получаем "сырые" байты ответа (SafeArray)
БайтыОтвета = WinHttp.ResponseBody;
// Инициализируем поток ADO в памяти
Поток = Новый COMОбъект("ADODB.Stream");
Поток.Type = 1; // adTypeBinary (работаем с двоичными данными)
Поток.Open();
Поток.Write(БайтыОтвета); // Записываем байты в поток
// Переключаем поток в текстовый режим для декодирования
Поток.Position = 0; // Возвращаем указатель в начало
Поток.Type = 2; // adTypeText (переключаем на чтение текста)
Поток.Charset = "windows-1251"; // Задаем правильную кодировку источника
// Читаем готовый раскодированный текст в переменную 1С
ТекстСтраницы = Поток.ReadText();
Поток.Close();
Сообщить(ТекстСтраницы);
Иначе
Сообщить("Ошибка HTTP: " + WinHttp.Status);
КонецЕсли;
Преимущества данного подхода: мы не создаем временных файлов на диске, не засоряем операционную систему и не тратим время на операции ввода-вывода (I/O), которые сильно замедляют работу системы при парсинге больших объемов данных.
---
Если по каким-то причинам предыдущие методы не могут быть реализованы (например, ограничения прав доступа к COM-объектам работы с памятью в вашей системе безопасности), можно воспользоваться схемой с записью бинарного тела во временный файл.
Разберем этот процесс по шагам:
WinHttp.
// Создаем уникальное имя для временного файла на диске
ИмяВходящегоФайла = ПолучитьВременныйФайл("html");
WinHttp = Новый COMОбъект("WinHttp.WinHttpRequest.5.1");
WinHttp.Open("GET", "http://example.com/page_win1251.html", Ложь);
WinHttp.Send();
Если WinHttp.Status = 200 Тогда
// Записываем бинарный ответ в файл через ADODB.Stream
Поток = Новый COMОбъект("ADODB.Stream");
Поток.Type = 1; // Бинарный режим
Поток.Open();
Поток.Write(WinHttp.ResponseBody);
Поток.SaveToFile(ИмяВходящегоФайла, 2); // 2 = overwriting existing file
Поток.Close();
// Читаем файл встроенными средствами 1С с указанием правильной кодировки
ТекстДок = Новый ТекстовыйДокумент;
ТекстДок.Прочитать(ИмяВходящегоФайла, "windows-1251");
ТекстСтраницы = ТекстДок.ПолучитьТекст();
// Удаляем временный файл после обработки
Попытка
УдалитьФайлы(ИмяВходящегоФайла);
Исключение
// Игнорируем ошибку удаления, если файл занят
КонецПопытки;
КонецЕсли;
Этот метод гарантирует, что 1С правильно интерпретирует каждый байт файла при чтении через ТекстовыйДокумент и на выходе мы получим чистую кириллицу без искажений.
Для эффективного парсинга сайтов и исключения багов с кодировками придерживайтесь следующих правил:
HTTPСоединение). Они избавят вас от проблем совместимости разрядностей 1С (32/64-bit) и операционных систем (Windows/Linux).ResponseText для не-UTF кодировок, если веб-сервер не отправляет заголовок кодировки корректно. Работайте напрямую с двоичным телом (ResponseBody или ПолучитьТелоКакДвоичныеДанные()).