Ноль, которого не видно: что Формат печатает пустотой
Разработка - Механизмы платформы 1С
В отчете колонка «Сумма». Две строки рядом: в первой пусто, во второй 0,00. Пользователь спрашивает, чем они отличаются. Мы в такой ситуации обычно лезем смотреть данные, а дело не в данных. Правильный ответ неприятен: в первой строке ровно ноль, а во второй — четыре тысячных.
Формат(0, "ЧДЦ=2") -> [] Формат(0.004, "ЧДЦ=2") -> [0,00]
То есть настоящий ноль платформа печатает пустотой, а почти ноль — нулем. Для читателя отчета это ровно наоборот: пустая ячейка выглядит как «данных нет», а 0,00 — как «данные есть, и это ноль».
Дальше — вся таблица замеров, ключи форматной строки и один ключ, который молча игнорируется из-за пробела. Во второй части то же самое про чтение отсутствующих значений: там пустота приходит оттуда, откуда ее не ждут.
Замеры 01.10.2026, платформа 8.5, Бухгалтерия 3.0.206, русская локализация.
Пустота по умолчанию
Ноль печатается пустотой не из-за формата, а вообще:
| Что печатаем | Результат |
|---|---|
Формат(0, "") |
пусто |
Формат(0, "ЧДЦ=2") |
пусто |
Формат(0, "ЧДЦ=2; ЧГ=0") |
пусто |
Формат(0.00, "ЧДЦ=2") |
пусто |
Формат(Неопределено, "ЧДЦ=2") |
пусто |
Формат("", "ЧДЦ=2") |
пусто |
Логика понятна: в печатных формах и отчетах нули чаще мешают, чем помогают, и платформа убирает их сама. Беда в том, что к нулю приравнялись и Неопределено, и пустая строка, а отличить их в готовом отчете уже нельзя — везде одинаковая пустая ячейка.
И тут же асимметрия, с которой статья началась:
| Значение | С ЧДЦ=2 |
|---|---|
0 |
пусто |
0.004 |
0,00 |
-0.004 |
0,00 |
0.005 |
0,01 |
Округление работает до проверки на ноль. Значение, которое округлилось в ноль, нулем для платформы не стало — она печатает его как 0,00. Поэтому в одной колонке соседствуют пустота и 0,00, и это не ошибка отчета.
Сколько видов пустоты
Четыре: ровно ноль, Неопределено, пустая строка и пустая дата. В готовой ячейке они неразличимы.
ЧН — это не формат, а текст
Лечится ключом ЧН. Вот как он себя ведет:
| Форматная строка | Результат |
|---|---|
ЧН=0 |
0 |
ЧН=0; ЧДЦ=2 |
0 |
ЧДЦ=2; ЧН=0 |
0 |
ЧН=0,00 |
0,00 |
ЧН=0.00 |
0.00 |
Смотрите на последние три строки. ЧН не форматирует ноль, а подставляет вместо него текст. Написали ЧН=0 — получите 0, без копеек, сколько бы ни стояло в ЧДЦ. Написали ЧН=0.00 с точкой — точка и напечатается, в русской локали, где разделитель запятая.
Отсюда практическое следствие. Чтобы получить в ячейке 0,00, писать надо ЧДЦ=2; ЧН=0,00 — то есть повторить формат руками внутри подстановки. ЧДЦ=2; ЧН=0 даст 0 и сломает выравнивание колонки по разрядам.
И еще: порядок ключей не важен, ЧН выигрывает у ЧДЦ в любом случае. А ЧВН= (вывод пустого числа) ноль не возвращает — остается пусто.
Ключ, который молчит из-за пробела
С пустой датой та же история, и чинится она ключом ДП. На этом я и сел.
Формат(Дата(1,1,1), "ДФ=dd.MM.yyyy; ДП=нет даты") -> [] Формат(Дата(1,1,1), "ДФ=dd.MM.yyyy; ДП='нет даты'") -> [нет даты]
Первая строка — не «ключ не работает». Это значение с пробелом без кавычек, и платформа в таком случае не ругается, а молча не применяет ключ. Результат ровно такой же, как если бы ДП там не было вовсе.
Без пробела все работает и без кавычек:
| Форматная строка | Результат |
|---|---|
ДП=нетданных |
нетданных |
ДП=- |
- |
ДФ=dd.MM.yyyy; ДП=- |
- |
ДЛФ=D; ДП=нетданных |
нетданных |
ДП='нет даты' |
нет даты |
ДП="нет даты" |
нет даты |
Кавычки годятся и одинарные, и двойные. Правило простое: есть пробел — ставьте кавычки, и не надейтесь, что про опечатку в форматной строке вам скажут.
Отдельно отмечу П= — такого ключа нет. Ни для даты, ни для числа он ничего не меняет, и, разумеется, молча.
Два случая, которые ломают сравнение
Пока были в форматной строке, замерились еще две вещи, которые обычно находят не там, где они живут.
Разряды разделяются неразрывным пробелом. Формат(1234567.89, "") дает 1 234 567,89, и пробелы там с кодом 160, а не 32. Такую строку не сравнить с той, что пришла из файла, и обратно в число она не превращается. Отключается через ЧГ=0.
Числовой формат не применяется к булеву. Формат(Ложь, "ЧДЦ=2") возвращает Нет. Если колонка динамически получает то число, то булево — а это обычное дело для вычисляемых полей с ВЫБОР, — в ней появятся Да и Нет там, где ждали цифры.
Заодно: Формат(-5, "ЧО=0") печатает (5). Отрицательное в скобках, без минуса. Для бухгалтерской формы это то, что нужно, а для выгрузки в файл — источник веселья на стороне получателя.
Часть вторая: пустота, которая приходит из чтения
Теперь про то же самое с другой стороны. Пустота в ячейке может прийти не из формата, а из того, что значение не прочиталось.
Привычная мысль: Структура.Свойство("Ключ", Знач) — безопасное чтение, вернет Ложь, если ключа нет, и переменную не тронет. Половина этой мысли неверна:
Знач = 999;
Нашлось = С.Свойство("Нету", Знач);
// Нашлось = Ложь, а Знач стало Неопределено
Переменная затирается. Не остается прежней, не остается 999 — становится Неопределено. Значит типовой прием «положу значение по умолчанию, а потом попробую прочитать» не работает: при отсутствии ключа значение по умолчанию исчезает.
Писать надо так:
Знач = 999;
Если С.Свойство("Нету") Тогда
Знач = С.Нету;
КонецЕсли;
То есть сначала проверить, потом читать. Либо не забывать восстанавливать значение после неудачного чтения — но первый вариант короче и не требует помнить про эту грабельку.
Остальные способы чтения ведут себя предсказуемо и единообразно:
| Что читаем, когда значения нет | Что возвращается |
|---|---|
Соответствие.Получить("Нету") |
Неопределено |
Массив.Найти("Нету") |
Неопределено |
ТаблицаЗначений.Найти("нету", "А") |
Неопределено |
Структура.Нету через точку |
исключение: «Поле объекта не обнаружено (Нету)» |
И отдельно про таблицы на форме, потому что они ведут себя не как таблицы значений. Реквизит формы объявлен как ТаблицаЗначений, а строка у него — ДанныеФормыЭлементКоллекции, тип другой:
| Что пробуем на таблице-реквизите формы | Что получаем |
|---|---|
ТаблицаДанных.Найти("нету", "А") |
метода нет: «Метод объекта не обнаружен (Найти)» |
Строка.Получить(0) |
метода нет |
Строка[0] = "значение" |
«Получение элемента по индексу для значения не определено» |
Строка["А"] = "значение" |
работает |
Строка.А = "значение" |
работает |
То есть привычный Найти на форме просто отсутствует, и искать строку приходится циклом. Это не пустота, это исключение — но попадаешь на него ровно тогда, когда переносишь на форму работающий серверный код.
Последняя строка таблицы выше — единственная, которая кричит. Все остальные тихо отдают Неопределено, которое потом попадет в сумму как ноль, а в отчет — как пустая ячейка. Круг замкнулся: та самая пустота из первой части статьи.
И обратно: ноль, записанный с копейками, не читается
Самое смешное в этой истории — что круг замыкается полностью. Напечатали ноль как 0,00, сохранили в файл, прочитали обратно — и ноль не вернулся.
В БСП есть СтроковыеФункцииКлиентСервер.СтрокаВЧисло, и берут ее именно потому, что она не бросает исключений и чинит то, чего не чинит Число(): обычный пробел между разрядами и бухгалтерские скобки. Вот что она делает на интересных входах:
| Вход | Число() |
СтрокаВЧисло |
|---|---|---|
-1 500 с обычным пробелом |
исключение | -1500 |
(1500) |
исключение | -1500 |
| пустая строка | исключение | 0 |
0 |
0 |
0 |
0,00 |
0 |
Неопределено |
Последняя строка и есть тихий угол. Настоящий ноль, записанный с копейками, возвращается как Неопределено — то есть неотличимо от мусора вроде abc. Пустая строка при этом честно даст ноль.
Причина в последней строке тела функции: результат возвращается только если он не равен нулю и строка после чистки не пуста. Ноль с копейками не проходит ни по первому условию, ни по второму.
Хорошая новость: Неопределено в арифметике падает сразу и громко. Плохая — ровно поэтому его часто оборачивают в ?(Рез = Неопределено, 0, Рез), и вот тут громкая ошибка снова становится тихим нулем. Минус U+2212 и тире U+2013 функция тоже не понимает, так что нормализация символов нужна все равно.
Эту грабельку подсказал читатель в комментариях к нашей прошлой статье, за что ему спасибо: сами мы на нее не вышли.
Как это искать в своем коде
Все описанное выше не ломается в момент написания. Оно ломается позже, когда в данные приходит настоящий ноль или пустая строка, а до тех пор код выглядит работающим и проходит любую проверку.
Поэтому искать надо не ошибки, а места, где ошибка станет возможной. Три приметы.
Вызов Формат без явного ключа для нуля. Если в форматной строке есть ЧДЦ, но нет ни ЧН, ни чего-то другого, что задает поведение на нуле, это место когда-нибудь напечатает пустоту. В отчете, где ноль означает «нет движения», пустой окажется ровно та строка, которую читатель будет искать глазами.
Значения ключей, в которых есть пробел. Пробел без кавычек превращает ключ в молчащий. Ищется это легко: смотреть в форматных строках пробел, стоящий после знака равенства и не взятый в кавычки. Платформа про такое не скажет ничего, ни при компиляции, ни при исполнении.
Собранная из кусков форматная строка. Когда форматную строку склеивают из переменных, увидеть глазами, что получилось, уже нельзя. Здесь помогает одно: перед вызовом записать готовую строку туда, где ее видно, и посмотреть на нее целиком хотя бы раз.
Чего этот замер не покрывает
Границы лучше назвать прямо, чтобы на статью не ссылались шире, чем она проверена.
Все замеры сняты на одной локализации и в одном типе сеанса. Про сеанс, где разделителем целой и дробной части служит точка, тут не утверждается ничего. И это не мелочь: соседний разбор показывает, что одна и та же строка в разных сеансах превращается в разные числа.
Поведение форматной строки в печатных формах и в схемах компоновки не проверялось. Там свой слой, и переносить на него выводы нельзя: то, что Формат в модуле печатает пустотой, не обязано совпадать с тем, что покажет ячейка макета или ресурс компоновки.
И последнее. Четыре утверждения про разбор строки в число взяты из прогона на другой платформе и в другой день. Они помечены отдельно не потому, что в них есть сомнение, а потому, что замер в одной пачке и замер в двух это разные вещи.
Что с этим делать
В отчетах задавать ЧН явно там, где ноль значащий. В остатках, в количестве, в разнице — пустая ячейка вместо нуля читается как «не считали». И писать ЧН=0,00, а не ЧН=0, если в колонке есть копейки.
Не принимать пустоту за отсутствие данных. Пустая ячейка в 1С значит одно из четырех: ровно ноль, Неопределено, пустая строка или пустая дата. Если это различие важно — печатайте его сами.
В форматной строке брать значения с пробелами в кавычки. И проверять результат глазами: об опечатке в форматной строке платформа не сообщает.
Безопасное чтение делать в два шага. Проверить наличие, потом читать. Одношаговый вариант со значением по умолчанию это значение теряет.
Помнить, что числа в строках приходят с неразрывными пробелами. При любом сравнении строк, при любой обратной конвертации — чистить. Код символа 160, а не 32, поэтому СтрЗаменить(Текст, " ", "") с обычным пробелом его не возьмет.
На форме не рассчитывать на Найти. Если код переносится из общего модуля в модуль формы и работает с таблицей-реквизитом — строку придется искать циклом, а индексировать по имени колонки, а не по номеру.
Отдельно скажу, чего мы не проверяли, чтобы не выдавать замер за правило. Все числа тут с одной платформы 8.5 и одной локализации, русской. Про 8.3 и про сеансы с другим разделителем ничего не утверждаю: асимметрия нуля и округления к локали отношения иметь не должна, но проверено это не было. Форматная строка в печатных формах и в схемах компоновки тоже может вести себя иначе — там свой слой, и он в этот замер не входил.
Про обратную сторону этой же истории — что платформа делает, когда в типизированный реквизит кладут значение не того типа, — у меня есть отдельный разбор: там "12,5" превращается в 12,5 или в 125 в зависимости от сеанса.
А какой ключ форматной строки вас подводил молча?
Вступайте в нашу телеграмм-группу Инфостарт
Вы можете заказать платную адаптацию этой статьи под ваши задачи на «Бирже заказов».
- 0% комиссии — оплата напрямую исполнителю;
- Исполнители любого масштаба — от отдельных специалистов до команд под проект;
- Прямой обмен контактами между заказчиком и исполнителем;
- Безопасная сделка — при необходимости;
- Рейтинги, кейсы и прозрачная система откликов.