Почему падает скрипт перекодировки из UTF-8 в Windows-1251 в 1С 7.7 и как это исправить?

Программист 1С v7.7
← На главную

При работе с файлами в 1С 7.7 часто возникает задача по изменению их кодировки, например, для соответствия требованиям форматов обмена. Классический способ — использование VBScript через объект MSScriptControl.ScriptControl. Однако такой подход может вести себя нестабильно: на одних файлах скрипт работает идеально, а на других падает с ошибкой «Ошибка выполнения Microsoft VBScript: Недопустимый вызов или аргумент процедуры». Давайте вместе разберемся, почему так происходит и как написать надежный код для конвертации.

Выясняем причину ошибки

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

В ходе отладки выясняется, что проблема не в путях к файлам и не в правах доступа. Если проанализировать содержимое «проблемного» файла, можно обнаружить в нем символы, которые есть в стандарте Unicode (и, соответственно, в кодировке UTF-8), но отсутствуют в целевой кодировке Windows-1251.

Ключевая причина — несовместимость символов. В рассматриваемом случае «виновником» оказался символ рубля (). Кодировка Windows-1251 была создана давно и не содержит многих современных символов, которые были добавлены в Unicode позже. Когда скрипт пытается записать в файл символ, для которого нет представления в Windows-1251, он не может этого сделать и генерирует ошибку «Недопустимый аргумент».

Это касается не только символа рубля, но и любых других символов, не входящих в стандартный набор CP1251: эмодзи, специфических математических или графических символов и т.д.

Анализируем исходный (неудачный) код

Давайте посмотрим на первоначальный вариант кода, который приводит к ошибке. Он использует комбинацию объектов MSScriptControl.ScriptControl, Scripting.FilesystemObject и ADODB.Stream.


Процедура глИзменитьКодовуюСтраницуФайла(имяФайла, исходнКодировка, нужнаяКодировка) Экспорт
	ScrptCtrl = СоздатьОбъект("MSScriptControl.ScriptControl");
	ScrptCtrl.Language = "VBScript";
	ScrptCtrl.AddCode("
		|Function StrConv(Text,SourceCharset,DestCharset)
		|	Set Stream=CreateObject(""ADODB.Stream"")
		|	Stream.Type=2
		|	Stream.Mode=3
		|	Stream.Open
		|	Stream.Charset=DestCharset
		|	Stream.WriteText Text
		|	Stream.Position=0
		|	Stream.Charset=SourceCharset
		|	StrConv=Stream.ReadText
		|End Function
		|
		|sub ConvertCodepage( fileName, SourceCharset, DestCharset )
		|	set fs = CreateObject(""Scripting.FilesystemObject"")
		|	originalText = fs.openTextFile(fileName,1).readAll()
		|	convertedText = strConv(originalText, SourceCharset, DestCharset )
		|	fs.openTextFile(fileName,2,true).write(convertedText)
		|end sub
		|
		|");
	CodeObject = ScrptCtrl.Modules("Global").CodeObject;
	CodeObject.ConvertCodepage(имяФайла, исходнКодировка, нужнаяКодировка);
КонецПроцедуры

Что здесь происходит:

  1. Текст файла полностью считывается в переменную originalText с помощью fs.openTextFile(fileName,1).readAll().
  2. Содержимое переменной преобразуется с помощью самописной функции StrConv, которая использует ADODB.Stream для конвертации строки в памяти.
  3. Результат convertedText записывается обратно в файл через fs.openTextFile(fileName,2,true).write(convertedText).

Слабое место — последняя строка. Метод write объекта FileSystemObject не умеет корректно обрабатывать ситуацию, когда в передаваемой строке есть неподдерживаемые символы для текущей системной кодировки. Он просто «падает» с ошибкой.

Надежное решение через ADODB.Stream

Чтобы избежать падения скрипта и обеспечить корректную обработку неподдерживаемых символов, следует отказаться от использования FileSystemObject для операций чтения/записи и полностью положиться на возможности объекта ADODB.Stream. Он умеет работать с файлами напрямую и более гибко управляет кодировками.

Итоговый рабочий код, который решает проблему, выглядит так:


Процедура ПреобразоватьКодировкуФайла(ИмяФайла, КодировкаОткуда, КодировкаКуда)
    // Создаем объект для работы с потоками данных
    ADODBStream = CreateObject("ADODB.Stream");

    // --- Шаг 1: Чтение исходного файла в правильной кодировке ---
    ADODBStream.Type = 2; // Тип потока: текстовый
    ADODBStream.Charset = КодировкаОткуда; // Указываем исходную кодировку (например, "UTF-8")
    ADODBStream.Open();
    
    // Загружаем содержимое файла в поток
    ADODBStream.LoadFromFile(ИмяФайла);
    
    // Считываем все содержимое потока в текстовую переменную
    Text = ADODBStream.ReadText();
    ADODBStream.Close();
    
    // --- Шаг 2: Запись текста в новой кодировке ---
    ADODBStream.Charset = КодировкаКуда; // Указываем целевую кодировку (например, "Windows-1251")
    ADODBStream.Open();
    
    // Записываем текст в поток
    ADODBStream.WriteText(Text);
    
    // Сохраняем содержимое потока в файл, перезаписывая его (параметр 2)
    ADODBStream.SaveToFile(ИмяФайла, 2);
    ADODBStream.Close();
    
    ADODBStream = "";
КонецПроцедуры

Проанализируем ключевые отличия этого подхода:

Важный момент: При таком подходе, если ADODB.Stream встречает символ, который невозможно представить в целевой кодировке, он не генерирует ошибку, а автоматически заменяет его на знак вопроса (?). Это позволяет сохранить целостность файла и успешно завершить операцию конвертации, что и требовалось для решения исходной задачи.

Альтернативные подходы и рекомендации

Помимо основного решения, стоит рассмотреть и другие полезные практики.

1. Использование временного файла. Чтение из файла и одновременная запись в него же — потенциально рискованная операция. Более надежный алгоритм — работать с временным файлом:

  1. Прочитать исходный файл.
  2. Записать преобразованные данные в новый временный файл (например, `temp_file.txt`).
  3. Если запись прошла успешно, удалить исходный файл.
  4. Переименовать временный файл в имя исходного.

Этот подход предотвращает потерю данных, если в процессе записи произойдет сбой.

2. Предварительная замена символов. Если вам нужно не просто заменить неподдерживаемые символы на «?», а на что-то осмысленное, можно перед конвертацией выполнить замену вручную средствами 1С. Например, заменить символ рубля на строку "руб.".


// ... код до считывания текста в переменную Text ...
Text = СтрЗаменить(Text, "₽", "руб.");
// ... далее код записи в новой кодировке ...

3. Использование внешних компонент. Для 1С 7.7 существует множество внешних компонент (например, FormEx, 1CPP, Karlik), которые предоставляют мощные и оптимизированные функции для работы со строками и кодировками. Часто их использование оказывается проще и быстрее, чем написание VBScript-оберток.

В заключение, проблема с падением скрипта перекодировки почти всегда связана с наличием в исходных данных символов, несовместимых с целевой кодировкой. Использование прямого функционала ADODB.Stream для загрузки и сохранения файлов — самый простой и надежный способ решить эту проблему в среде 1С 7.7.

← На главную