Кодировка текстового файла в 1С: проверяем байты и читаем без кракозябр

· 11 мин

Содержание

Кодировку файла узнают по его первым байтам и по тому, какие байты встречаются в тексте. EF BB BF в начале — это UTF-8 с меткой BOM. FF FE — UTF-16. Если метки нет, файл проверяют на корректность UTF-8. Не прошёл проверку — почти наверняка Windows-1251 или, для старых DOS-выгрузок, CP866. В 1С 8.3 такую проверку пишут на встроенном языке через буфер двоичных данных, а результат передают в ТекстовыйДокумент.Прочитать() или ЧтениеТекста.

Типичная ситуация: банк, маркетплейс или коллега прислали CSV, обработка загрузки отработала без ошибок, а в справочнике контрагентов появились «РћРћРћ Ромашка». Файл прочитан не в той кодировке. Ниже — как узнать кодировку файла руками за минуту, как определить её программно в 1С и как исправить чтение, конвертацию, CSV и XML.

Как определить кодировку файла без программирования

Прежде чем писать код, проверьте один конкретный файл вручную. Так вы поймёте, с чем имеете дело, и сможете проверить свой алгоритм.

ИнструментГде смотретьЧто покажет
Блокнот Windows 10/11строка состояния внизу справа«UTF-8», «UTF-8 с BOM», «ANSI», «UTF-16 LE»
Notepad++меню Кодировки (Encoding), отмечен текущий варианткодировку и наличие BOM, можно переключить и сразу увидеть результат
VS Codeстрока состояния, справакодировку; по клику — «Reopen with Encoding»
PowerShellFormat-Hex -Path .\file.csvшестнадцатеричный дамп: видно BOM и байты кириллицы
Linuxfile -bi file.csvtext/plain; charset=utf-8 или charset=iso-8859-1

Последний пункт с подвохом. Утилита file не знает Windows-1251 и для кириллицы в этой кодировке часто пишет iso-8859-1 или unknown-8bit. Читайте это как «однобайтовая, не UTF-8».

«ANSI» в Блокноте на русской Windows — это Windows-1251. Именно в ней Excel по умолчанию сохраняет CSV, а 1С называет её КодировкаТекста.ANSI.

Почему 1С читает файл кракозябрами

Сам файл почти никогда не «битый». Байты в нём правильные, но программа сопоставляет им символы по другой таблице. По виду мусора можно сразу догадаться, какая пара кодировок перепутана.

Что видно вместо «Привет»Файл в кодировкеПрочитан как
ПриветUTF-8Windows-1251
������ или пустые квадратыWindows-1251UTF-8
ЏаЁўҐвCP866Windows-1251
п»їПривет в начале первой строкиUTF-8 с BOMWindows-1251
Пробелы или нули между буквамиUTF-16однобайтовая

Откуда берутся такие ошибки при обмене данными:

  • 📤 Источник сменил кодировку. Сайт или маркетплейс перешёл на UTF-8, а обработка загрузки по-прежнему читает в ANSI.
  • 💾 Пересохранение в Excel. Пользователь открыл CSV, поправил ячейку, нажал «Сохранить» — UTF-8 превратился в Windows-1251.
  • 🧾 Старое ПО и оборудование. Кассовые программы, банк-клиенты и DOS-утилиты до сих пор выгружают в CP866.
  • 🔤 Жёстко прописанная кодировка в коде. Разработчик однажды указал КодировкаТекста.UTF8, и с тех пор все файлы читаются только так.
  • 🏷 Неверная декларация в XML. В заголовке написано encoding="windows-1251", а файл сохранён в UTF-8.
Диагностика: выберите симптом

Чтение файла в 1С с явным указанием кодировки

Главное правило: не полагайтесь на то, что платформа угадает кодировку сама. Автоопределение надёжно работает только при наличии BOM. Для файла без метки всегда передавайте кодировку явно.

В 1С кодировку задают двумя способами:

  1. Значением системного перечисления КодировкаТекста: UTF8, ANSI (Windows-1251 на русской системе), OEM (CP866), UTF16, Системная.
  2. Строкой с именем кодировки: "utf-8", "windows-1251", "cp866", "koi8-r".

Строка удобнее, когда кодировку определяет код или она хранится в настройках. И она не зависит от языковых настроек сервера: ANSI на сервере с английской локалью может оказаться вовсе не 1251.

Чтение целиком через текстовый документ:

ТекстДок = Новый ТекстовыйДокумент;
ТекстДок.Прочитать(ИмяФайла, "windows-1251");

Для НомерСтроки = 1 По ТекстДок.КоличествоСтрок() Цикл
	Строка = ТекстДок.ПолучитьСтроку(НомерСтроки);
	// разбор строки
КонецЦикла;

Построчное чтение большого файла через ЧтениеТекста — оно не держит весь файл в памяти:

Чтение = Новый ЧтениеТекста(ИмяФайла, КодировкаТекста.UTF8);
Строка = Чтение.ПрочитатьСтроку();
Пока Строка <> Неопределено Цикл
	// разбор строки
	Строка = Чтение.ПрочитатьСтроку();
КонецЦикла;
Чтение.Закрыть();

Код с ИмяФайла выполняется там, где лежит файл. Если пользователь выбирает файл у себя на компьютере, а разбор идёт на сервере, сначала передайте файл на сервер (НачатьПомещениеФайлаНаСервер или ПоместитьФайл), а на сервере получите двоичные данные из временного хранилища и работайте уже с ними.

Если на входе двоичные данные, а не путь, строку из них получают так:

Текст = ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, "windows-1251");

Программное определение кодировки файла в 1С по байтам

Когда файлы приходят из разных источников и кодировка заранее неизвестна, её определяют кодом. Алгоритм в три шага:

  1. Проверить BOM. Метка однозначна: EF BB BF (239, 187, 191) — UTF-8, FF FE (255, 254) — UTF-16LE, FE FF (254, 255) — UTF-16BE.
  2. Проверить, корректный ли это UTF-8. У UTF-8 строгие правила: ведущий байт задаёт длину символа, а следующие байты обязаны лежать в диапазоне 128–191. Файл в Windows-1251 с кириллицей эту проверку практически никогда не проходит.
  3. Отличить Windows-1251 от CP866. В 1251 русские буквы занимают байты 192–255. В CP866 большая часть букв лежит в 128–175. Достаточно посчитать, каких байтов больше.

Буфер двоичных данных и обращение к байту по индексу доступны в платформе 8.3.9 и новее. Код ниже работает на сервере и на клиенте.

// Возвращает имя кодировки, которое можно передать в ТекстовыйДокумент.Прочитать()
Функция ОпределитьКодировкуФайла(ИмяФайла) Экспорт

	ДД = Новый ДвоичныеДанные(ИмяФайла);
	Буфер = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(ДД);
	Размер = Буфер.Размер;

	// Шаг 1. Метка порядка байтов (BOM)
	Если Размер >= 3 И Буфер[0] = 239 И Буфер[1] = 187 И Буфер[2] = 191 Тогда
		Возврат "utf-8";
	ИначеЕсли Размер >= 2 И Буфер[0] = 255 И Буфер[1] = 254 Тогда
		Возврат "utf-16le";
	ИначеЕсли Размер >= 2 И Буфер[0] = 254 И Буфер[1] = 255 Тогда
		Возврат "utf-16be";
	КонецЕсли;

	// Анализируем не больше 64 КБ: для решения этого достаточно
	Граница = Мин(Размер, 65536);

	// Шаг 2. Корректный UTF-8 без BOM
	Если ЭтоКорректныйUTF8(Буфер, Граница) Тогда
		Возврат "utf-8"; // сюда же попадёт чистый ASCII - он совместим с UTF-8
	КонецЕсли;

	// Шаг 3. Windows-1251 или CP866
	Байты1251 = 0;
	Байты866 = 0;
	Для Индекс = 0 По Граница - 1 Цикл
		Байт = Буфер[Индекс];
		Если Байт >= 192 Тогда
			Байты1251 = Байты1251 + 1;
		ИначеЕсли Байт >= 128 И Байт <= 175 Тогда
			Байты866 = Байты866 + 1;
		КонецЕсли;
	КонецЦикла;

	Если Байты866 > Байты1251 Тогда
		Возврат "cp866";
	КонецЕсли;
	Возврат "windows-1251";

КонецФункции

Функция ЭтоКорректныйUTF8(Буфер, Граница)

	Позиция = 0;
	Пока Позиция < Граница Цикл
		Байт = Буфер[Позиция];
		Если Байт < 128 Тогда
			Хвост = 0;
		ИначеЕсли Байт >= 194 И Байт <= 223 Тогда
			Хвост = 1;
		ИначеЕсли Байт >= 224 И Байт <= 239 Тогда
			Хвост = 2;
		ИначеЕсли Байт >= 240 И Байт <= 244 Тогда
			Хвост = 3;
		Иначе
			Возврат Ложь; // такой ведущий байт в UTF-8 невозможен
		КонецЕсли;

		Если Позиция + Хвост >= Граница Тогда
			Прервать; // символ разрезан границей анализа - не считаем ошибкой
		КонецЕсли;

		Для Сч = 1 По Хвост Цикл
			Следующий = Буфер[Позиция + Сч];
			Если Следующий < 128 Или Следующий > 191 Тогда
				Возврат Ложь;
			КонецЕсли;
		КонецЦикла;

		Позиция = Позиция + Хвост + 1;
	КонецЦикла;

	Возврат Истина;

КонецФункции

Использование:

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

Почему 64 КБ, а не весь файл. Цикл по байтам на встроенном языке медленный: на файле в десятки мегабайт он займёт заметное время. Первых 64 КБ хватает, чтобы встретить сотни русских букв и принять решение.

Ограничения метода, о которых нужно помнить:

  • 🔸 Файл из одной латиницы и цифр определится как UTF-8. Это не ошибка: такие байты одинаковы во всех трёх кодировках.
  • 🔸 KOI8-R алгоритм не распознаёт и вернёт windows-1251. Если такие файлы реально приходят, добавьте отдельный счётчик или храните кодировку в настройках источника.
  • 🔸 Конструктор Новый ДвоичныеДанные(ИмяФайла) читает файл целиком в память. Для файлов в сотни мегабайт читайте начало через ЧтениеДанных.
Перед запуском загрузки на реальных данных
Выполнено: 0 / 5

Конвертация текста: перекодировка из Windows-1251 в UTF-8 и обратно

Перекодировать файл в 1С можно без внешних утилит. Принцип один: прочитать байты в исходной кодировке в строку, а строку записать в нужной.

Через двоичные данные — с полным контролем над BOM:

ДД = Новый ДвоичныеДанные(ИмяИсходного);
Текст = ПолучитьСтрокуИзДвоичныхДанных(ДД, "windows-1251");

// Третий параметр - писать ли BOM. Ложь - файл без метки
ДДРезультат = ПолучитьДвоичныеДанныеИзСтроки(Текст, КодировкаТекста.UTF8, Ложь);
ДДРезультат.Записать(ИмяРезультата);

Через текстовый документ — короче, но с меньшим контролем:

ТекстДок = Новый ТекстовыйДокумент;
ТекстДок.Прочитать(ИмяИсходного, "cp866");
ТекстДок.Записать(ИмяРезультата, "windows-1251");

При записи в UTF-8 через ТекстовыйДокумент.Записать метка BOM, как правило, добавляется. Если получатель её не ждёт, проверьте первые байты результата или пишите через ПолучитьДвоичныеДанныеИзСтроки с явным Ложь.

Обратная перекодировка из UTF-8 в Windows-1251 может терять символы. Буквы казахского и украинского алфавитов частично, эмодзи, иероглифы, некоторые типографские знаки в 1251 отсутствуют. Они пропадут или превратятся в «?». Перед отправкой файла в систему, принимающую только 1251, просмотрите итог.

Если нужно перекодировать сотни файлов разом вне 1С, быстрее сделать это в консоли. В Linux: iconv -f WINDOWS-1251 -t UTF-8 in.csv > out.csv.

Диагностика проблем с файлами CSV и XML

CSV

CSV — самый частый источник жалоб, потому что формат не хранит кодировку нигде. Что важно знать о его происхождении:

  • 📊 Excel при сохранении как «CSV (разделители — запятые)» пишет файл в Windows-1251, а разделителем на русской Windows ставит точку с запятой.
  • 📊 Вариант «CSV UTF-8» в новых версиях Excel пишет UTF-8 с BOM.
  • 🌐 Выгрузки сайтов, маркетплейсов и облачных сервисов почти всегда в UTF-8, часто без BOM.

Отсюда частая ловушка. Код ищет колонку по заголовку ИНН, а в файле с BOM первая строка начинается с невидимого символа U+FEFF. Сравнение «ИНН» = «ИНН» даёт Ложь, и загрузка пишет «колонка не найдена». Решение — удалить символ после чтения:

ПерваяСтрока = СтрЗаменить(ТекстДок.ПолучитьСтроку(1), Символ(65279), "");
Колонки = СтрРазделить(ПерваяСтрока, ";");

XML

С XML ситуация обратная: кодировка указана в самом файле, в декларации <?xml version="1.0" encoding="windows-1251"?>. ЧтениеXML ей доверяет. Если декларации нет, по стандарту XML файл считается UTF-8.

Проблемы начинаются, когда декларация не совпадает с реальными байтами. Например, файл выгрузили в 1251 и вручную пересохранили в UTF-8 текстовым редактором, а строку encoding не поправили. Парсер падает с ошибкой разбора или выдаёт мусор.

Порядок диагностики:

  1. Откройте файл в Notepad++ и сравните реальную кодировку со строкой encoding.
  2. Если отличаются — исправьте декларацию в источнике выгрузки. Это правильное решение.
  3. Если источник не исправить, прочитайте файл как текст в настоящей кодировке и передайте строку в ЧтениеXML.УстановитьСтроку(). Байты к этому моменту уже раскодированы, и декларация не помешает.

При записи XML кодировку задают сразу: ЗаписьXML.ОткрытьФайл(ИмяФайла, "UTF-8"). Платформа пропишет её и в байтах, и в декларации.

Файл обмена с банком

Отдельный случай — текстовый файл обмена с клиент-банком в формате 1CClientBankExchange. В его заголовке есть строка Кодировка=Windows или Кодировка=DOS. Если выписка не загружается или назначения платежей нечитаемы, сравните эту строку с реальной кодировкой файла и с кодировкой в настройке обмена с банком на стороне 1С. Они должны совпадать.

Текстовый документ: вывод на экран и печать

Бывает, что файл прочитан, но проблема видна только при показе или печати. Здесь работает простое правило: кодировка исправляется при чтении, а не при выводе. Если текст уже попал в строку кракозябрами, никакая настройка шрифта или принтера его не вернёт.

Чтобы быстро показать пользователю содержимое файла, используйте:

ТекстДок = Новый ТекстовыйДокумент;
ТекстДок.Прочитать(ИмяФайла, ОпределитьКодировкуФайла(ИмяФайла));
ТекстДок.Показать("Содержимое файла");

Файл откроется во встроенном текстовом редакторе 1С, откуда его можно распечатать командой печати из главного меню.

Что учесть при выводе и печати:

  • 🖨 Выравнивание колонок. Текстовые отчёты из старых программ выровнены пробелами. Для них нужен моноширинный шрифт, иначе таблица «поплывёт».
  • 🖨 Псевдографика. Рамки из символов ┌─┐ есть в CP866, но их нет в Windows-1251. Такой файл читайте только как cp866, иначе рамки превратятся в русские буквы.
  • 🖨 Печатная форма. Если текст нужно печатать регулярно и в аккуратном виде, выводите его не в текстовый, а в табличный документ: там управляются поля, шрифт и ориентация страницы.
  • 🖨 Файл для оборудования. Некоторые кассовые и весовые программы принимают задания только в CP866. Записывайте их через ТекстовыйДокумент.Записать(ИмяФайла, КодировкаТекста.OEM).

Частые вопросы

Можно ли определить кодировку со стопроцентной точностью?
Только при наличии BOM. Без метки любое определение — это вероятностная оценка по байтам. Для UTF-8 она очень надёжна, а различить Windows-1251 и KOI8-R без анализа частот букв нельзя.
Чем отличается КодировкаТекста.ANSI от строки "windows-1251"?
ANSI — это кодовая страница операционной системы, на которой выполняется код. На русской Windows это 1251, на сервере с другой локалью может быть иначе. Строка «windows-1251» однозначна везде.
Нужно ли убирать BOM из файлов для других систем?
Зависит от получателя. Excel BOM любит: без него он откроет UTF-8 кракозябрами. Многие веб-сервисы и скрипты, наоборот, воспринимают BOM как часть первого поля. Уточните требования в документации к формату.
Что делать, если платформа старше 8.3.9?
Буфера двоичных данных там нет. Определите кодировку внешним средством (Notepad++, Format-Hex) и сохраните её в настройках обработки для конкретного источника файлов.
Почему один и тот же файл у коллеги открывается нормально, а у меня нет?
Скорее всего, вы открываете его разными программами или код выполняется на разных компьютерах с разной системной кодировкой. Уберите из кода КодировкаТекста.Системная и ANSI, указывайте кодировку строкой.