Разработка мобильных приложений на платформе 1С:Предприятие часто преподносит неприятные сюрпризы, связанные с кроссплатформенностью. Один из таких классических багов — некорректная обработка национальных символов (например, украинских букв «і», «ї», «є») при чтении XML-файлов в устаревшей однобайтовой кодировке windows-1251. Настольная версия программы может отображать все данные корректно, однако при запуске того же кода на мобильном клиенте или в мобильном приложении (под управлением Android или iOS) вместо нужных букв начинают появляться другие символы.
В этой статье мы подробно разберем, почему возникает эта проблема, проанализируем внутренние механизмы платформы и рассмотрим несколько эффективных способов ее решения: от простого пересохранения файла на диске до оптимизированного декодирования «на лету» в оперативной памяти мобильного устройства.
Для начала давайте разберем физическую суть проблемы. В стандартной кодовой таблице windows-1251 байтовые коды некоторых украинских букв расположены вплотную друг к другу:
U+0456) в кодировке windows-1251 кодируется байтом 0xB3 (в десятичной системе — 179).U+0491) кодируется соседним байтом 0xB4 (в десятичной системе — 180).Мобильные операционные системы изначально спроектированы для работы исключительно с многобайтовой кодировкой UTF-8. Поддержка устаревших региональных однобайтовых кодировок (таких как windows-1251) в мобильных ОС реализуется через сторонние библиотеки трансляции (например, ICU — International Components for Unicode). При чтении файла через объект ЧтениеXML мобильная платформа 1С пытается сопоставить байты источника с таблицей символов Юникода. Из-за внутренней ошибки смещения или некорректной реализации декодера в мобильной платформе байт 0xB3 ошибочно интерпретируется как соседний 0xB4. В результате в строковые переменные 1С вместо буквы «і» записывается буква «ґ».
Примечательно, что если прочитать этот же файл обычным текстовым парсером ЧтениеТекста с явным указанием кодировки, декодирование на мобильном устройстве отрабатывает без ошибок. Это связано с тем, что механизмы чтения XML и обычного текста используют разные программные модули ядра платформы.
Самый очевидный способ, к которому пришел автор темы на форуме, — это предварительное текстовое чтение «проблемного» файла, исправление заголовка XML с указанием новой кодировки и перезапись файла на диск в формате UTF-8.
Давайте подробно разберем этот алгоритм по шагам:
ЧтениеТекста, явно указав кодировку "windows-1251". В этом режиме мобильная платформа корректно преобразует байты в строку Юникода.UTF-8, нам обязательно нужно скорректировать заголовок XML (декларацию), заменив строку encoding="windows-1251" на encoding="UTF-8". Если этого не сделать, парсер ЧтениеXML выдаст ошибку несоответствия реальной кодировки заявленной.ЗаписьТекста с кодировкой "UTF-8".ЧтениеXML — все украинские символы распознаются корректно.Посмотрим на пример реализации данного метода в виде функции:
&НаСервереБезКонтекста
Функция ПересохранитьИПрочитатьФайл(ПутьКФайлу)
// Шаг 1: Читаем исходный файл в правильной кодировке windows-1251
ТекстовыйЧтец = Новый ЧтениеТекста(ПутьКФайлу, "windows-1251");
СодержимоеФайла = ТекстовыйЧтец.Прочитать();
ТекстовыйЧтец.Закрыть();
// Шаг 2: Корректируем декларацию XML-документа
СодержимоеФайла = СтрЗаменить(СодержимоеФайла, "windows-1251", "UTF-8");
СодержимоеФайла = СтрЗаменить(СодержимоеФайла, "WINDOWS-1251", "UTF-8");
// Шаг 3: Перезаписываем файл на диск в кодировке UTF-8
ТекстовыйПисатель = Новый ЗаписьТекста(ПутьКФайлу, КодировкаТекста.UTF8);
ТекстовыйПисатель.Записать(СодержимоеФайла);
ТекстовыйПисатель.Закрыть();
// Шаг 4: Теперь файл готов к корректному разбору парсером XML
Парсер = Новый ЧтениеXML;
Парсер.ОткрытьФайл(ПутьКФайлу);
Пока Парсер.Прочитать() Цикл
// Здесь выполняется стандартная обработка узлов XML
Если Парсер.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
Наименование = Парсер.ЗначениеАтрибута("Naimenovanie");
// Теперь украинские символы (включая "і") отображаются корректно!
КонецЕсли;
КонецЦикла;
Парсер.Закрыть();
КонецФункции
Данный подход полностью решает проблему, однако имеет существенный недостаток: выполнение лишних операций записи на диск мобильного устройства. На мобильных телефонах и планшетах дисковые операции ввода-вывода (I/O) являются крайне медленными, а частая перезапись файлов приводит к преждевременному износу флеш-памяти.
Чтобы избежать медленных дисковых операций, мы можем выполнить перекодирование файла прямо в оперативной памяти мобильного устройства. Для этого воспользуемся возможностями работы с двоичными данными, которые появились в платформе 1С 8.3.
Проанализируем этот изящный метод по шагам:
ДвоичныеДанные. Эта операция выполняется очень быстро, так как платформа просто считывает поток байтов без их анализа и декодирования.ПолучитьСтрокуИзДвоичныхДанных(), передавая туда наш объект двоичных данных и явно указывая кодировку "windows-1251". Системные декодеры этого метода работают гораздо стабильнее, чем внутренний транслятор объекта ЧтениеXML, поэтому на выходе мы получаем абсолютно чистую и корректную строку Юникода без искажения украинских букв.УстановитьСтроку() объекта ЧтениеXML вместо метода ОткрытьФайл(). Это позволяет передать готовую строку напрямую парсеру без промежуточного сохранения на флеш-накопитель устройства.Рассмотрим пример кода, реализующий этот высокопроизводительный вариант:
&НаКлиентеИлиНаСервере
Процедура БыстроеЧтениеXMLБезПерезаписи(ПутьКФайлу)
// Шаг 1: Загружаем файл как массив байтов
ФайлДвоичныеДанные = Новый ДвоичныеДанные(ПутьКФайлу);
// Шаг 2: Преобразуем байты в строку с корректным декодированием windows-1251
СтрокаXML = ПолучитьСтрокуИзДвоичныхДанных(ФайлДвоичныеДанные, "windows-1251");
// Шаг 3: Загружаем декодированную строку в парсер XML
Парсер = Новый ЧтениеXML;
Парсер.УстановитьСтроку(СтрокаXML);
// Шаг 4: Читаем данные XML из оперативной памяти
Пока Парсер.Прочитать() Цикл
Если Парсер.ТипУзла = ТипУзлаXML.НачалоЭлемента И Парсер.Имя = "Товар" Тогда
Наименование = Парсер.ЗначениеАтрибута("Naimenovanie");
// Все символы отображаются корректно, дисковые операции минимизированы!
КонецЕсли;
КонецЦикла;
Парсер.Закрыть();
КонецПроцедуры
Этот способ является наиболее предпочтительным для мобильных платформ 1С, так как он экономит ресурсы процессора, не забивает кэш файловой системы временными файлами и работает в несколько раз быстрее первого метода.
Многие разработчики при возникновении подобных ошибок пытаются принудительно переопределить свойства КодировкаXML или КодировкаИсточника у объекта ЧтениеXML перед началом парсинга. Важно знать, что в платформе 1С эти свойства инициализируются только после вызова первого метода чтения Прочитать(). Попытки изменить эти параметры вручную до вызова чтения на мобильном клиенте чаще всего просто игнорируются системой, и платформа автоматически откатывается к кодировке по умолчанию (которая на мобильных устройствах не может корректно обработать windows-1251).
Несмотря на то, что приведенные выше костыли и обходные пути гарантированно решают проблему «здесь и сейчас», стандарт разработки мобильных приложений 1С настоятельно рекомендует полностью исключить передачу файлов в однобайтовых кодировках (таких как windows-1251, IBM866, ISO-8859-5) на мобильные устройства.
При проектировании любых механизмов интеграции, мобильных обменов, выгрузок данных из учетных систем (будь то XML, JSON или простые текстовые файлы) всегда используйте кодировку UTF-8 (желательно без метки BOM). Переход на UTF-8 является единственным стандартом, который гарантирует абсолютно идентичное отображение любых национальных алфавитов и спецсимволов на любых операционных системах — Windows, Linux, macOS, Android и iOS — без необходимости написания сложных процедур перекодирования.