При обмене данными часто возникает ситуация, когда стандартные средства разделения строк, такие как СтрРазделить, не справляются с поставленной задачей. Проблема заключается в структуре формата CSV: внутри текстового поля могут находиться те же символы, что используются в качестве разделителей (запятые) или ограничителей (кавычки). Если просто разбивать строку по запятой, мы получим разорванные данные, которые невозможно корректно интерпретировать. Разберем, как решить эту задачу в 1С наиболее эффективно.
Рассмотрим типовую строку, которая вызывает сложности: "Имя контрагента ""ООО Копыта""", 42, "Адрес, город, улица". Простой парсер увидит запятую внутри адреса и разделит его на три разные части. Кроме того, числа часто передаются без кавычек, а строки — в двойных кавычках, внутри которых могут быть еще одни кавычки. Наша задача — получить массив, где каждый элемент соответствует логическому полю, независимо от того, что находится внутри него.
Регулярные выражения позволяют описать сложную логику поиска разделителей, которые находятся вне кавычек. Проанализируем один из наиболее лаконичных паттернов, предложенных в обсуждении:
Паттерн: ("([^"]*)"|[^,]*)(,|$)
Этот паттерн работает по следующему алгоритму: он ищет либо блок текста, заключенный в кавычки, либо любую последовательность символов до ближайшей запятой или конца строки. Рассмотрим, как применить это в коде 1С через COM-объект VBScript.RegExp.
// Создаем объект регулярных выражений
RegExp = Новый COMОбъект("VBScript.RegExp");
RegExp.IgnoreCase = Истина;
RegExp.Global = Истина;
RegExp.MultiLine = Истина;
// Устанавливаем паттерн
RegExp.Pattern = "(""([^""]*)""|[^,]*)(,|$)";
// Исходная строка
ИсходнаяСтрока = "ПоступлениеТМЦ,""Имя контрагента """"ООО Копыта"""""",42";
// Выполняем поиск
Matches = RegExp.Execute(ИсходнаяСтрока);
Для Каждого Match Из Matches Цикл
// Значение поля находится в первой подгруппе захвата
ЗначениеПоля = Match.SubMatches(0);
// Дополнительная очистка от обрамляющих кавычек
ЗначениеПоля = СокрЛП(ЗначениеПоля);
Если Лев(ЗначениеПоля, 1) = """" И Прав(ЗначениеПоля, 1) = """" Тогда
ЗначениеПоля = Сред(ЗначениеПоля, 2, СтрДлина(ЗначениеПоля) - 2);
КонецЕсли;
Сообщить(ЗначениеПоля);
КонецЦикла;
Если ваши данные содержат экранированные кавычки (удвоенные кавычки внутри строки), предыдущий паттерн может работать некорректно. Проанализируем более продвинутый вариант, который учитывает специфику стандарта CSV:
Паттерн: (?<=^|,)(?:"([^"]*(?:""[^"]*)*)"|([^,",]*))(?=,|$)
Этот вариант использует механизмы "просмотра вперед" и "просмотра назад", что позволяет точнее определять границы полей. Он разделяет захват на две группы: первая для данных в кавычках (с учетом удвоенных кавычек), вторая — для чисел и простых строк без кавычек.
Начиная с версии 8.3.18, в платформе 1С появился специализированный объект ЧтениеCSV. Это самый быстрый и надежный способ. Выясним, почему его стоит предпочесть регулярным выражениям:
Посмотрим на пример использования этого объекта:
// Настройка параметров чтения
ПараметрыЧтения = Новый ПараметрыЧтенияCSV;
ПараметрыЧтения.РазделительПолей = ",";
ПараметрыЧтения.СимволЦитаты = """";
Чтение = Новый ЧтениеCSV;
// Если данные в строке, используем ЧтениеТекста как посредник
Текст = Новый ЧтениеТекста;
Текст.УстановитьСодержимое(ИсходнаяСтрока);
Чтение.Открыть(Текст, ПараметрыЧтения);
МассивРезультат = Новый Массив;
Пока Чтение.Прочитать() Цикл
// Чтение возвращает массив строк для каждой строки CSV
ДанныеСтроки = Чтение.Контекст;
Для Каждого Поле Из ДанныеСтроки Цикл
МассивРезультат.Добавить(Поле);
КонецЦикла;
КонецЦикла;
Если вам нужно обработать десятки тысяч строк (как упоминал автор темы), и вы работаете в среде Windows, можно использовать ADODB.Recordset. Этот метод позволяет обращаться к текстовому файлу как к таблице базы данных. Проанализируем этот подход:
Преимущества:
Connection = Новый COMОбъект("ADODB.Connection");
Recordset = Новый COMОбъект("ADODB.Recordset");
// Указываем путь к папке с файлом
ПутьКПапке = "C:\Data\";
Файл = "data.csv";
СтрокаПодключения = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + ПутьКПапке + ";Extended Properties=""text;HDR=No;FMT=Delimited"";";
Connection.Open(СтрокаПодключения);
// Выполняем SQL-запрос к текстовому файлу
Recordset.Open("SELECT * FROM [" + Файл + "]", Connection);
Пока НЕ Recordset.EOF Цикл
Для НомерПоля = 0 По Recordset.Fields.Count - 1 Цикл
Значение = Recordset.Fields.Item(НомерПоля).Value;
// Обработка значения
КонецЦикла;
Recordset.MoveNext();
КонецЦикла;
Если вы все же решили использовать регулярные выражения, помните о производительности. Создание объекта COMОбъект("VBScript.RegExp") внутри цикла по строкам — это критическая ошибка, которая замедлит обработку в сотни раз. Рассмотрим правильный алгоритм:
RegExp один раз перед началом цикла.Pattern).Execute.В ситуациях, когда исходные данные крайне "грязные" (например, кавычки не закрыты или расставлены хаотично), регулярные выражения могут не справиться. В таких случаях мы рекомендуем использовать метод "Автомата состояний" — посимвольный обход строки с флагом ВнутриКавычек. Это гарантирует 100% контроль над логикой парсинга.