1С и чувствительность к регистру [поход на грабли]

Публикация № 995591

Программирование - Практика программирования

ошибки программирования

33
Всем известно, что исполняемый код для платформы 1С не чувствителен к регистру символов. Некоторый особенные люди считают себя одаренными и пользуются этой возможностью, чтобы писать в своем уникальном стиле либо все маленькими буквами, либо наоборот большими. Оставим эти глупости на совести таких разработчиков, ведь нам же главное не "красота" в режиме конфигуратора, а чтобы обрабатываемые нами данные оставались аутентичными. Что бы "А" (код 1040) и "а" (код 1072) или "T" (код 84) и "t" (код 116) всегда оставались сами собой и превращались друг в друга только под нашим чутким контролем с помощью ВРег() и НРег(). К сожалению, бывает не всегда так, что может приводить к неожиданным ошибкам.

Предыстория

Как-то при переносе данных в одну животноводческую базу, я столкнулся со странным поведением выполнения довольно простого кода. У меня было множество животных с уникальными шифрами, описывающими их породу. Те, кто помнят школьные уроки по генетики, знают, что доминантные гены принято изображать большими буквами, а рецессивные - маленькими. Для остальных небольшая иллюстрация для понимания:

 
 размер имеет значение

Казалось бы, что загрузку написал правильно, но результат в базе не соответствовал данным источника - данные по некоторым животным были перепутаны, а по части вообще ничего не загрузилось. Пристальное изучение несоответствий показало, что путаница наблюдались в тех случаях, когда шифры животных различались не составом, а только регистром.

 

Поиск по данным в базе

Давайте попробуем воссоздать пример с картинки выше и создадим справочник "Виды цветов" с тремя элементами: АА -> красный, Аа -> розовый и аа -> белый. Проблему можно увидеть сразу, если попытаться наш код внести в стандартное поле "Код":

 
 скрин

Заметим, что таким образом мы можем задать элементу справочника код А00001 и при автонумерации получим А00002, А00003 и так далее. Так же мы можем задать код а00001 и получить а00002, а00003... Но если мы при наличии А00001 по какой-то причине захотим установить номер а00001, то получить "облом".

Аналогичное поведение при тестировании кодов/номеров я обнаружел у документов, задач, бизнес-процессов, планов видов характеристик, планов счетов и планов обмена. Но у планов видов расчетов, однако, разрешается создать одновременно элементы с номерами "А00001" и "а00001", что очень странно - тут можно было бы сослаться на то, что у плана видов расчетов в настройках отсутствует свойство включения/отключения контроля уникальности номера, но этого свойства так же нет и у плана обменов. В документации о такой выборочно действующей особенности поведения ничего не написано. Если я просто не увидел, то напишите в комментариях.

Кстати, поскольку удалось создать несколько видов расчета с идентичными кодами, то это прекрасный повод проверить результат функции НайтиПоКоду(). Только предварительно я воспользуюсь отсутствием у плана видов расчетов контроля уникальности номеров и добавлю еще один элемент с большой "А" - интересно какой из этих двух элементов будет выбран:

 
 скрин

Да, уж. Согласитесь, результат оказался неожиданным и он подтверждает ранее замеченное наблюдение, что для платформы 1С регистр символов как минимум в кодах/номерах значения не умеет. Выходит, создавая код на нашей платформе, программист получает по факту выполнения: 1040 = КодСимвола("А") = КодСимвола("а") = 1072 , и лишь конечный пользователь системы видит на экране реальные символы.

Но продолжим нашу проверку. Обычно на практике для всяких внешних кодов используют реквизиты - в моем случае было так же. Создадим такой для справочника "Виды цветов" и попробуем воспользоваться другим стандартным поисковым методом - НайтиПоРеквизиту():

 
 скрин

Как раз с этим я и столкнулся при переносе - в поиске по реквизиту регистр символов игнорируется.

Хотя, как оказывается, не обязательно быть программистом, что бы испытать дискомфорт при точном поиске - аналогичное поведение наблюдается и при использовании отборов СКД в динамическом списке (видимо одна и та же поисковая функция из внутренней библиотеки):

 
 скрин

Для полноты картины, давайте протестируем последний оставшийся метод - ПоискПоНаименованию() и получим тот же результат (даже требование "точного соответствия" не помогло):

 
 скрин

Итак, поисковые методы менеджеров объектов отказываются правильно искать чувствительные к регистру символов данные. А можно ли самостоятельно создать подобные методы с помощью механизма запросов? Давайте попробуем:

 
 скрины

Как видите, ни использования оператора "=" в тексте запроса, ни оператор "Подобно" не помогли - каждый раз выбираются все похожие элементы, игнорируя регистры символов.

Т.е. для текста запроса, который транслируется в SQL и выполняется во внешних СУБД, снова верно выражение: 1040 = КодСимвола("А") = КодСимвола("а") = 1072. Я уже приготовился, что все во что я верил ложно и в мире 1С будет справделиво ("А" = "а") = Истина, но к счастью хотя бы примитивное сравнение строк работает и нужную нам функцию все же можно создать:

 
 скрин

 

Поиск по коллекциям

С данными базы как мы уже убедились - грустно. А как дело обстоит с коллекциями? 

Массив - обнаружена чувствительность к регистру символов в обычном и фиксированном вариантах для метода Найти().

 
 код и результаты

Таблица значений - методы Найти() и НайтиСтроки() чувствительны к регистру.

 
 код и результаты

Список значений - метод НайтиПоЗначению() чувствителен к регистру символов.

 
 код и результаты

Структура - поисковые методы отсутствуют, а ключи регистр игнорируют.

 
код и результаты 

Соответствие - поисковые методы отсутствуют, но зато поддерживаются ключи в разных регистрах.

 
код и результаты 

 

Заключение

Я понимаю, что разработчики платформы хотели упростить работу бабушек из бухгалтерии, что бы те не тянулись к Shift (или даже CapsLock) и при наборе "вова" в поле ввода у них сразу выбрался "Вова". Только я отказываюсь понимать - почему из-за этого "облегчения труда" должны страдать разработчики конфигураций. Зачем и нам навязали отсутствие у строк регистра? Ведь именно нам-то как раз "вова" и "Вова" различать нужно (или пути в линуксах, или буквы "м" и "М" в форматных строках, или...). Да и самим пользователям иногда в списке нужно найти единственного человека с фамилией "Кар", а не увидеть сотню макарычей.

Выход, как мы видим, существует. Тут можно написать поисковую функцию с перепроверкой результата. Еще можно вместо поиска перед основным алгоритмом создать соответствие, где по строковым ключам загнать значение соответствующих ссылок. Но хотелось бы применять подобные костыли реже.

P.S. Если кто-нибудь до сих пор этого не знал, то открою тайну - пароль на вход в 1С тоже нечувствителен к регистру! ;)

33

См. также

Специальные предложения

Комментарии
Избранное Подписка Сортировка: Древо
1. PowerBoy 2893 04.02.19 11:10 Сейчас в теме
Зачем и нам навязали отсутствие у строк регистра?

Разработчики 1С не виноваты, скорей разработчики MS SQL Serverа!
3. DenisCh 04.02.19 11:44 Сейчас в теме
(1) Как не виновны, если они сами коллейшн регистронезависимый для базы ставят?
А скуль умеет и так, и так...
5. Dementor 405 04.02.19 17:14 Сейчас в теме
(1) такое же поведение в файловой базе.
2. fxmike 36 04.02.19 11:16 Сейчас в теме
Господи, пароль не чувствителен к регистру! Сколько лет я был в неведении! Как теперь с этим жить? Почему? Зачем? о_О
TreeDogNight; awk; serg_gres; KroVladS; Orlando Skibraves; badboychik; Dementor; acanta; +8 Ответить
13. Дмитрий74Чел 103 05.02.19 14:03 Сейчас в теме
(2) На сколько помню в конфигураторе есть флаг, который включает контроль.
Прикрепленные файлы:
4. A_Max 16 04.02.19 12:38 Сейчас в теме
1. Всё это поведение описано в документации и вполне логично понимается где будет регистрозависимый "поиск" в зависимости от используемого объекта.
2. Про пароли ещё интересней. Хранится два хэша: от правильного пароля и от приведённого в верхний (или нижний уже точно не вспомню) регистр. Но для чего это сделано всё равно загадка) Наверно чтобы подбирать было проще. А ещё из-за кривой реализации сохранения рядом с хэшами можно было наблюдать пароль вообще в открытом виде!!!!
Dementor; acanta; Glebis; +3 Ответить
16. SeiOkami 668 08.02.19 09:09 Сейчас в теме
(4)
Хранится два хэша: от правильного пароля и от приведённого в верхний (или нижний уже точно не вспомню) регистр. Но для чего это сделано всё равно загадка) Наверно чтобы подбирать было проще.


Это чтобы, в зависимости от настройки в базе, сравнивать либо с чувствительностью к регистру, либо без.
Если без чувствительности, то введённый пользователем пароль вводится в верхний регистр и его хэш сравнивается с хэшом в базе.
6. zqzq 16 05.02.19 09:24 Сейчас в теме
P.S. Если кто-нибудь до сих пор этого не знал, то открою тайну - пароль на вход в 1С тоже нечувствителен к регистру! ;)

Кстати, не совсем так, из справки:

Конфигуратор 1С:Предприятие 8. Параметры информационной базы
...
Проверка сложности паролей пользователей. Если данный параметр установлен, пароли пользователей должны удовлетворять следующим требованиям:
длина пароля не должна быть менее значения, указанного в параметре Минимальная длина паролей пользователей;
пароль должен состоять из символов, относящихся как минимум к трем из перечисленных групп:
заглавные буквы;
строчные буквы;
цифры;
специальные символы ;
пароль не должен совпадать с именем пользователя;
пароль не должен являться последовательностью символов.
Если параметр не установлен, то проверка пароля в процессе аутентификации регистронезависима.
Использование ограничений на пароли пользователей информационной базы не влияет на существующие пароли. Ограничения будут применены только при изменении существующего пароля или при добавлении нового пользователя информационной базы.
Показать
Дмитрий74Чел; Dementor; +2 Ответить
8. Dementor 405 05.02.19 12:41 Сейчас в теме
(6) вот только про эту настройку знают единицы. За более чем десятилетнюю практику видел использование такой настройки только один раз. На крупных компаниях применяют доменную авторизацию, а в мелких предпочитают использовать стандартные пароли: "1", "123"... или вообще входят без паролей (если в базе только директор и главбух).
7. qwinter 574 05.02.19 09:54 Сейчас в теме
Выход, как мы видим, существует. Тут можно написать поисковую функцию с перепроверкой результата. Еще можно вместо поиска перед основным алгоритмом создать соответствие, где по строковым ключам загнать значение соответствующих ссылок. Но хотелось бы применять подобные костыли реже.
Зачем такие сложности? Если очень надо храните двоичные данные строки в строковом реквизите. Это элементарный код. Функции ПолучитьДвоичныеДанныеИзСтроки, ПолучитьДвоичныеДанныеИзHexСтроки и ПолучитьСтрокуИзДвоичныхДанных спасут ваш мир)))
9. Dementor 405 05.02.19 12:43 Сейчас в теме
(7) тоже была такая идея, но не стал использоваться - хотел оставить для пользователя нормальную видимость.
10. qwinter 574 05.02.19 13:22 Сейчас в теме
(9) может я конечно чего то не учитываю, но не совсем понимаю проблемы сделать для пользователя нормальную видимость.
12. qwinter 574 05.02.19 13:45 Сейчас в теме
(9) Единственный минус, если поле слишком длинное, то индексировать не получиться. Максимальная срока 630 символов при котором поле 1с индексируется, а это получается 157 символов для поля с регистром. Так что реквизит формы желательно будет ограничить, если будет требоваться получать список выбора по полю.
11. qwinter 574 05.02.19 13:36 Сейчас в теме
(9) На форме
&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
	
	НаименованиеСРегистром = ПолучитьСтрокуИзДвоичныхДанных(ПолучитьДвоичныеДанныеИзHexСтроки(ТекущийОбъект.ПолеДД));
	
КонецПроцедуры

&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
	
	ТекущийОбъект.ПолеДД = ПолучитьДвоичныеДанныеИзСтроки(НаименованиеСРегистром);
	
КонецПроцедуры
Показать

И если требуется в модуле менеджера, что бы выводить поле в представление или что бы подбор с регистром работал
Процедура ОбработкаПолученияПредставления(Данные, Представление, СтандартнаяОбработка)
	
	СтандартнаяОбработка = Ложь;
	Представление = ПолучитьСтрокуИзДвоичныхДанных(ПолучитьДвоичныеДанныеИзHexСтроки(Данные.ПолеДД));
	
КонецПроцедуры

Процедура ОбработкаПолученияДанныхВыбора(ДанныеВыбора, Параметры, СтандартнаяОбработка)
	
	СтандартнаяОбработка = Ложь;
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ ПЕРВЫЕ 10
		|	Города.Ссылка КАК Ссылка
		|ИЗ
		|	Справочник.Города КАК Города
		|ГДЕ
		|	Города.ПолеДД ПОДОБНО &ПолеДД";
	
	Запрос.УстановитьПараметр("ПолеДД", Строка(ПолучитьДвоичныеДанныеИзСтроки(Параметры.СтрокаПоиска)) + "%");
	
	РезультатЗапроса = Запрос.Выполнить();
	
	Выборка = РезультатЗапроса.Выбрать();
	
	ДанныеВыбора = Новый СписокЗначений;
	Пока Выборка.Следующий() Цикл
		ДанныеВыбора.Добавить(Выборка.Ссылка);
	КонецЦикла;
	
КонецПроцедуры

Процедура ОбработкаПолученияПолейПредставления(Поля, СтандартнаяОбработка)
	
	СтандартнаяОбработка = Ложь;
	Поля.Добавить("ПолеДД");
	
КонецПроцедуры

Показать
14. Dementor 405 06.02.19 11:28 Сейчас в теме
(11) для поля наименования покатит (если представление по наименованию) и в динамических списках выводить не Наименование, а Ссылка. А что с реквизитами? На формах списков/выборов писать варианты ПриПолученииДанных (для ОФ и УФ по разному) и делать функции общих модулей для расчета представления полей в отчетах на компоновке? Как-то уж слишком большая цена за возможность регистрозависимого поиска - дешевле в модуле менеджера справочника дописать свою поисковую функцию.
15. qwinter 574 06.02.19 13:52 Сейчас в теме
(14)
А что с реквизитами? На формах списков/выборов писать варианты ПриПолученииДанных (для ОФ и УФ по разному
Зачем вообще такой реквизит нужен, если по нему не делается ввод по строке? Или не делается поиск в формах списка/выбора? Искать по наименованию в модулях? Я промолчу, что я об этом думаю.
делать функции общих модулей для расчета представления полей в отчетах на компоновке?
Зачем? Эти функции можно использовать в вычисляемых полях.
дешевле в модуле менеджера справочника дописать свою поисковую функцию.
Считывание лишних данных всегда дороже. К тому же отсутствие возможности создания поиска в формах списка/выбора.
17. SeiOkami 668 08.02.19 09:13 Сейчас в теме
А как сделать так, чтобы отборы СКД и динамических списков у пользователей "чувствительно" отрабатывали?
18. Dementor 405 10.02.19 14:50 Сейчас в теме
(17) к счастью, класс задач с чувствительностью к регистру не очень широкий.
В тех случаях, когда нужно на формах делать регистрочувствительный фильтр - нужно делать этот фильтр самому. В зависимости от данных есть разные пути. Для небольшой выборки, когда отклонений много - запросом и повторным сравнением отобрать нужные ссылки и наложить на динамический список отбор по Ссылка ВСписке. Если выборка большая и мало отклонений - оставить Реквизит Равно/Подобно/Содержит, а с помощью функции отобрать именно отклонения для второго условия отбора - Ссылка НеВСписке.
Оставьте свое сообщение