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

Программист 1С v7.7 Управленческий учет Торговля и дистрибуция
← На главную

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

Разбор логической ошибки: ловушка оператора ИЛИ

Проанализируем ситуацию, с которой столкнулся автор темы. Перед ним стояла задача: пропустить строки файла, если код операции не равен «11» и не равен «56». Автор написал следующее условие:


Если (КодОперации <> "11") ИЛИ (КодОперации <> "56") Тогда
    Продолжить;
КонецЕсли;

На первый взгляд кажется, что код должен работать. Однако на практике это условие всегда будет истинным, какой бы код операции ни пришел на вход. Давайте выясним причину, рассмотрев разные варианты значения переменной КодОперации:

  1. Пусть КодОперации равен "11". Проверяем первую часть: "11" <> "11" — это Ложь. Проверяем вторую часть: "11" <> "56" — это Истина. Поскольку используется оператор ИЛИ, общее выражение (Ложь ИЛИ Истина) дает Истину. Программа выполняет Продолжить.
  2. Пусть КодОперации равен "56". Проверяем первую часть: "56" <> "11" — это Истина. Вторую часть можно даже не проверять (хотя в 7.7 вычисляются обе части), результат уже будет Истина. Программа снова выполняет Продолжить.
  3. Пусть КодОперации равен "100". Обе части выражения истинны. Результат — Истина.

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


Если (КодОперации <> "11") И (КодОперации <> "56") Тогда
    Продолжить;
КонецЕсли;

Также можно воспользоваться законом Де Моргана и инвертировать условие равенства:


Если НЕ ((КодОперации = "11") ИЛИ (КодОперации = "56")) Тогда
    Продолжить;
КонецЕсли;

Почему программа зависает: проблема вложенных циклов

В процессе обсуждения выяснилось, что при исправлении условия программа начала «виснуть». Рассмотрим подробнее фрагмент кода функции СформироватьМПАЙ, представленный автором:


Для А = 4 по ФайлЗагрузки.КоличествоСтрок() Цикл
    // ... разбор строки ...
    Иначе //здесь обработка отмены
        Для А=1 По 50 Цикл
             // ... какие-то действия ...
             Прервать;
        КонецЦикла;
    КонецЕсли;
КонецЦикла;

Проанализируем ситуацию: здесь используется переменная А в качестве счетчика внешнего цикла (перебор строк файла) и эта же самая переменная А используется во внутреннем цикле. Это критическая ошибка для 1С 7.7.

Когда выполнение заходит во внутренний цикл, значение переменной А сбрасывается в 1. После завершения внутреннего цикла (или выхода по Прервать), управление возвращается во внешний цикл. Внешний цикл видит, что его счетчик А теперь равен, например, 1 или 5, что значительно меньше, чем общее количество строк в файле. В результате цикл начинает обрабатывать одни и те же строки заново, что визуально воспринимается как «бесконечный цикл» или зависание.

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

Оптимизация разбора текстовых файлов (CSV)

В коде автора используется метод последовательного поиска разделителя (точки с запятой) и обрезания строки:


Поз = Найти(СтрокаИзФайла,";");
КодОперации = Лев(СтрокаИзФайла,Поз - 1);
СтрокаИзФайла = Сред(СтрокаИзФайла,Поз+1);

Хотя этот метод рабочий, он громоздок и неудобен в отладке. Рассмотрим более элегантные способы разбора строки в 1С 7.7:

Способ 1: Использование СтрЗаменить и СтрПолучитьСтроку

Мы можем заменить все точки с запятой на символы переноса строк. Тогда к каждому полю можно будет обратиться по индексу:


ПодготовленнаяСтрока = СтрЗаменить(СтрокаИзФайла, ";", РазделительСтрок);
КодОперации = СтрПолучитьСтроку(ПодготовленнаяСтрока, 4); // Например, код операции в 4-й колонке
ДатаОперации = СтрПолучитьСтроку(ПодготовленнаяСтрока, 2);

Способ 2: СписокЗначений и ИзСтрокиСРазделителями

Этот метод часто работает быстрее при большом количестве колонок:


Сп = СоздатьОбъект("СписокЗначений");
// Превращаем строку в формат, который понимает 1С (значения в кавычках через запятую)
ТекСтрока = """" + СтрЗаменить(СтрокаИзФайла, ";", """,""") + """";
Сп.ИзСтрокиСРазделителями(ТекСтрока);
КодОперации = Сп.ПолучитьЗначение(4);

Подведем итоги

Для успешного решения задачи загрузки данных из кассового ПО в 1С 7.7 мы выяснили следующее:

  1. Логика условий: Для фильтрации («пропустить все, кроме Х и Y») используйте И с оператором «не равно» или НЕ с оператором ИЛИ внутри скобок.
  2. Изоляция циклов: Всегда используйте уникальные имена переменных-счетчиков для вложенных конструкций Для ... Цикл.
  3. Методы разбора: Старайтесь избегать многократного использования Найти и Сред для одной строки; преобразование строки в многострочный текст или использование СписокЗначений делает код чище и понятнее.
  4. Отладка: Если программа «зависла», используйте замер производительности или точки останова (breakpoint), чтобы увидеть текущее значение счетчика цикла.

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

← На главную