Простая и быстрая хэш функция (hash) средствами 1С




Принцип обмена данными из 1С с сайтом (на MySQL) и выдачи (публикации) этих данных по запросу.
PHP-Скрипт автоматической загрузки данных из файла данных в формате CSV в базу данных сайта работающего на WordPress.

В продолжение моей темы: 1С:Альфа-Авто Автосалон Автосервис: обмен с сайтом.
С помощью данного скрипта можно загружать в автоматическом режиме, по расписанию, данные сервисных книжек (ремонтов авто) из 1С:Альфа-Авто Автосалон Автосервис.
Также можно загружать данные в ручном режиме: для этого делается скрытая страница, где размещается специальная кнопка.
Комментарии размещенные внутри скрипта разъяснят логику и порядок действия.
Комментарии с "/////    echo" использовались для отладки.
Дополнительно создана таблица для журналирования результатов загрузки данных.
Скрипт включает в себя защиту от SQL инъекций (думаю безопасность соблюдена в полной мере).
В кратце:
1. Пишется скрипт, который запускает этот.
2. Создается регламентное задание в WordPress, по которому запускается скрипт из п.1. 
3. Этот скрипт осуществляет проверку на существование файла обмена в папке.
4. Если данные не новые, загрузка не производится.
5. Если данные новые, очищается таблица сервисных книжек.
6. Загружаются новые данные.

Собственно сам скрипт:

<?php // Полная загрузка сервисных книжек, создан 2025-01-05 12:44:55

global $wpdb2;
global $failure;
global $file_hist;

/////  echo '<H2><b>Старт загрузки</b></H2><br>';

$failure=FALSE;
//подключаемся к базе
$wpdb2 = include_once 'connection.php'; ; // подключаемся к MySQL
// если не удалось подключиться, и нужно оборвать PHP с сообщением об этой ошибке
if (!empty($wpdb2->error))
{
/////   echo '<H2><b>Ошибка подключения к БД, завершение.</b></H2><br>';
$failure=TRUE;
wp_die( $wpdb2->error );
}

$m_size_file=0;
$m_mtime_file=0;
$m_comment='';
/////проверка существования файлов выгрузки из 1С
////файл выгрузки сервисных книжек
$file_hist = ABSPATH.'/_1c_alfa_exchange/AA_hist.csv';
if (!file_exists($file_hist))
{
/////   echo '<H2><b>Файл обмена с сервисными книжками не существует.</b></H2><br>';
$m_comment='Файл обмена с сервисными книжками не существует';
$failure=TRUE;
}

/////инициируем таблицу лога
/////если не существует файла то возврат и ничего не делаем
if ($failure){
///включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']
/////   echo '<H2><b>Попытка вставить запись в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>$m_comment));
wp_die();
/////    echo '<H2><b>Возврат в начало.</b></H2><br>';
return $failure;
}
/////проверка лога загрузки, что бы не загружать тоже самое
$masiv_data_file=stat($file_hist);   ////передаем в массив свойство файла
$m_size_file=$masiv_data_file[7];    ////получаем размер файла
$m_mtime_file=$masiv_data_file[9];   ////получаем дату модификации файла
////создаем запрос на получение последней удачной загрузки
////выбираем по штампу времени создания (редактирования) файла загрузки AA_hist.csv, $m_mtime_file

/////   echo '<H2><b>Размер файла: '.$m_size_file.'</b></H2><br>';
/////   echo '<H2><b>Штамп времени файла: '.$m_mtime_file.'</b></H2><br>';
/////   echo '<H2><b>Формирование запроса на выборку из лога</b></H2><br>';
////препарируем запрос
$text_zaprosa=$wpdb2->prepare("SELECT * FROM `vin_logs` WHERE `last_mtime_upload` = %s", $m_mtime_file);
$results=$wpdb2->get_results($text_zaprosa);

if ($results)
{   foreach ( $results as $r)
{
////если штамп времени и размер файла совпадают, возврат
if (($r->last_mtime_upload==$m_mtime_file) && ($r->last_size_upload==$m_size_file))
{////echo '<H2><b>Возврат в начало, т.к. найдена запись в логе.</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>'Загрузка отменена, новых данных нет, т.к. найдена запись в логе.'));
wp_die();
return $failure;
}
}
}
////если данные новые, пишем в лог запись о начале загрузки
/////echo '<H2><b>Попытка вставить запись о начале загрузки в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>0, 'last_size_upload'=>$m_size_file, 'comment'=>'Начало загрузки'));

////очищаем таблицу
$clear_tbl_zap=$wpdb2->prepare("TRUNCATE TABLE %s", 'vin_history');
$clear_tbl_zap_repl=str_replace("'","`",$clear_tbl_zap);
$results=$wpdb2->query($clear_tbl_zap_repl);
/////   echo '<H2><b>Очистка таблицы сервисных книжек</b></H2><br>';
if (empty($results))
{
/////   echo '<H2><b>Ошибка очистки таблицы книжек, завершение.</b></H2><br>';
//// если очистка не удалась, возврат
$failure=TRUE;
wp_die();
return $failure;
}

////загружаем данные
$table='vin_history';         // Имя таблицы для импорта
//$file_hist Имя CSV файла, откуда берется информация     // (путь от корня web-сервера)
$delim=';';          // Разделитель полей в CSV файле
$enclosed='"';      // Кавычки для содержимого полей
$escaped='\

44 Comments

  1. artbear

    Интересно, плюсанул.

    (0) Но также очень интересно практическое применение:

    цитата «Вроде бы не нужна в 1С хэш функция, а всё таки иногда без неё не обойтись.

    В частности для индексирования строк неограниченной длины или групп строк»

    вот увидеть бы пример индексирования и сравнить со встроенными методами 1С (например, соответствие) !

    Reply
  2. kosilov

    А попробуй в запросе поставить ГДЕ <строка неограниченной длины> = «Слово из 3-х букв» и поймешь.

    Строка неограниченной длины — реквизит объекта неоганиченной длины.

    Посмотри зачем нужна хэш функция.

    Сравнивать длинные текстовые поля так эффективнее.

    Reply
  3. artbear

    (2) Пример с ГДЕ не подходит.

    Я в таких случаях юзаю внутри запроса

    ГДЕ

    ВЫРАЗИТЬ(ИнвентаризацияОС.Комментарий КАК Строка(200)) = &Комментарий

    запрос прекрасно работает.

    Покажи пример индексирования и поиска

    ЗЫ я прекрасно знаю назначения хеш-функций 🙂

    Большой опыт программирования на С++ никуда не денется

    Reply
  4. Adept

    (3) пример, буквально сегодня применю у себя в конфе,

    Есть справочник юзерей, и справочник ролей, они само собой связаны, для того что бы не лезть в БД и каждый раз не проверять есть ли у меня там все необходимые юзеры и роли, те что и в конфе, я буду их собирать в строку и хешировать, и сравнивать с хешем скажем в константе и если уже хеши отличаются то лезть в бд и синхронизировать юзерей и роли.

    Вот так например 🙂

    Reply
  5. artbear

    (4) Нифига не понял пример.

    Расшифруй примерчик.

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

    Жду.

    Reply
  6. Adept

    (5) Все просто, при запуске предприятия проводиться проверка соответствие справочника пользователи с тч роли и справочника роли, текущему состоянию (пользователям и ролям внесенными в конфигуратор) , проводиться проверка соответствия если его нет то справочники синхронизируются с системой, для того что бы не сверять при каждом входе со справочниками (строить запросы бд), просто сохранить хэш из строки с именами и ролями пользователей, проверять при входе соответствие хэша, если были изменения, уже лезть в БД и искать где надо добавить пользователей или изменить состав ролей и тд.

    Reply
  7. artbear

    (6) 1. Перед тем, как сверять, ты же все равно должен исходные значения получить, значит, все равно будет запрос к БД.

    (0) Автор, дай пример использования.

    Я не против разработки, плюсанул сразу. мне интересно, где именно в 1С можно юзать подобную хеш-функцию.

    Ведь у 1С уже есть Соответствие и Структура для быстрого поиска, а также Индексы у таблиц.

    Всем — помните, что значения хеш-а совершенно не обязательно должны быть уникальными 🙁 При работе с хеш-функцией вполне могут быть коллизии, об этом нужно всегда помнить!

    Reply
  8. kosilov

    (7) Я сейчас разрабатываю аналитическую базу данных, в которой в качестве источника аналитики используются RSS каналы.

    RSS генератор, не отслеживает какие новости пользоватль прочитал (получил), а какие нет. Т.е. это задача RSS приемника определить какая новость уже есть в базе, а какая нет.

    Некоторые каналы пересылают поле (тэг) id новости, некоторые нет.

    Соответственно, для таких каналов, которые не пересылают тэг id (да и вобщем-то для всех для надежности) необходимо как-то определить новая это новость или нет. Для этого само собой используются некоторый набор тэгов, таких как title, дата публкации и пр. Этот набор тэгов выбирает пользователь отдельно для каждого канала, а каналов может быть много. Здесь есть две проблемы.

    С одной стороны не очень приятный запрос, где на лету должны генериться условия (т.е. в тексте), во вторых некоторые поля неограниченной длины. Можно конечно брать подстроку, но подстрока — это всегда ограничение. Дляна заголовка может теоретически быть до 2кб., а я возьму только 500 символов (это ограничение 1С).

    В третьих, при большом количестве записей и условий, где сравниваются строки, скорость выполнения запроса будет низкой, а запрос мне надо сделать на каждую новость, которых в канале от 10 до 20 (и больше). Соответственно при получении новостей от 10-20 каналов в которых 10-20 новостей придется выполнить 200-400 запросов, каждый из которых может выполняться секунд по 10.

    При хэшировании размер индекса (длина) составлет всего 10-20 символов (вместо 500) и он один для всех текстовых и не текстовых полей. Кроме этого хэш поле индексировано в базе (текстовых поля неограниченной длины нельзя индексировать).

    В результате использования хэш функции имеем следующие преимущества:

    1. Не надо генерить текст запроса на лету

    2. Нет ограничений на длину индексируемой текстовой строки

    3. Многократный выигрышь в скорости.

    Это по моей задаче.

    Есть другой пример.

    Допустим вы храните в базе данных текстовые документы (приказы, письма, книги, статьи и пр). Тут уж точно нельзя ограничиваться 200 или 500 первыми символами, так как они могут совпадать для различых текстов. Выход здесь будет один — хэшировать текст.

    Отдельная задача — индексирование информации помещаемой в хранилище значений. Файлы, картинки, архивы. Иногда тоже требуется проверить, есть уже такой объект в хранилище либо нет. И здесь хэш — самый простой выход.

    Что же касается возможности коллизии хэш, то такая вероятность практически равна нулю. Коллизии могут возникать только при умышленной подделки исходного текста, или при очень коротком хэш (например 16 бит)

    Если же нет опасности, что кто-то будет подстраиваться под хэш и хэш длинный, то коллизии не будет. Вспомните информацию про GUID. При его длине вероятность совпадения ближайшие сколько-то лет (50 что ли) практически равна нулю.

    Reply
  9. artbear

    (8) Спасибо, хорошие примеры.

    ОФФ По поводу ГУИД кто-то на ИС или еще где 🙂 выкладывал статью, где показывалось, что не слишком уж случайное число получается 🙂

    Reply
  10. artbear

    (8) Слегка бы поправить функции для оптимизиции и исключения ошибок.

    1. непонятно, как поведет себя функция ХэшБлоками, если длина блока будет больше, чем длина строки — Сред обрежет строку или нет, не помню/не знаю 🙁

    2. ИМХО 1С переменную окончания цикла каждый раз пересчитывает, поэтому СтрДлина лучше считать в отдельной переменной до цикла.

    Лучше всего проверить функцию на автоматических тестах, которые совсем несложно сделать.

    Reply
  11. kosilov

    (10) Всё верно. У меня была ошибка. При смене блока пропускался один символ за счет цикла Для. Исправил.

    Что касается Сред(«йцукен»,1,1000), то он работает корректно. В данном примере будет просто выбрана вся строка.

    Reply
  12. artbear

    (11) Вот я и говорю, лучше проверять на автоматических тестах.

    Тем более, задача простая, и тестами ее охватить легко 🙂

    Reply
  13. artbear

    (11) По вычислении СтрДлина при каждой итерации поправь.

    все-таки хеш-функция должна быть максимально быстрой 🙂

    Reply
  14. kosilov

    (13) Поправил 🙂

    Reply
  15. alexk-is

    (14) Я подозревал, что у меня шустрый компьютер, только не знал, что на столько. А ведь ему уже 3 года.

    …подшаманил чуток 🙂

    Reply
  16. kosilov

    (15) Боюсь в код закралась ошибка после последней правки.

    В функции хэшБлоками было ДлинаСтроки = СтрДлина(СтрокаХэш) вместо ДлинаСтроки = СтрДлина(Строка) (как правильно и как сейчас).

    Странно что 1С не ругнулась, но думаю так как СтрокаХэш в этой функции неопределено, то длина была 0 соответственно у Вас хэш высчитывался один рах для всего текста, а не 2-3 раза.

    Попробуйте теперь. 🙂

    У меня на ноуте 10тыс. для текста анонса.

    Reply
  17. alexk-is

    (16) Спасибо подправил. Замер для текста на картинке. Жаль, что у меня нет ноута 🙂

    Reply
  18. artbear

    (15), (16) что правили-то?

    Reply
  19. alexk-is

    (18) Внутренности конечно. Ну, и внешний вид чуток.

    Reply
  20. kosilov

    (18) Я же написал: В функции хэшБлоками было ДлинаСтроки = СтрДлина(СтрокаХэш) вместо ДлинаСтроки = СтрДлина(Строка) (как правильно и как сейчас).

    Reply
  21. alexk-is

    (18) (20) Во вложении вариант с некоторыми изменениями оптимизированный под 8.1.

    Суть изменений

    1. Исправлена ошибка в фунции ХэшБлокамиСОбраткой

    2. Добавлена возможность сохранения текста

    3. Начало замера с начала секунды

    Суть оптимизации

    1. Незначительная оптимизация кода

    2. Сокращено количество команд выполняемых интерпретатором (наиболее заметно при использовании отладчика с включенным замером производительности)

    3. Подобран более оптимальный размер блока, как мне кажется

    Reply
  22. kosilov

    (21) Скачал. Посмотрю сейчас

    Reply
  23. kosilov

    (21) Скрин говорит сам за себя. Плюс однозначно.

    PS Никогда бы не подумал, что написание текста модуля в одну строку улучшает производительность 😀

    Reply
  24. kosilov

    Забавно, но еще 2%-3% прироста скорости можно получить переведя текст на анлийский вариант.

    Reply
  25. alexk-is

    (23) Не совсем так. Всё дело в переходах между командами. Поэтому если смотреть в замере производительности, то цикл в строке ДЛЯ из 10000 повторов записанный обычной структурой будет выполнен 10001 раз, а записанный в 1 строку 1 раз. Разумеется, что при этом результат выполнения будет одинаковый.

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

    Reply
  26. kosilov

    Есть тут ещеодин момент, незнаю как онотразится на стойкости функции.

    Изначально функция расчитывалась (смотри ссылку на теорию в заголовке) на анлийский алфавит (или по крайней мере на 255 символов). У нас для русских букв мы имеем юникод 2 байта (КодСимвола(Сред(СтрокаХэш,к,1)) для русской буквы даст число больше 255).

    Reply
  27. alexk-is

    КодСимвола(«z») = 122

    КодСимвола(«я») = 1103

    КодСимвола(«ё») = 1105

    Reply
  28. tormozit

    Может кому пригодится такой вариант подсчета хеша. http://www.forum.mista.ru/topic.php?id=483601

    Reply
  29. GSoft

    Ребят а кто делал или встречал функции шифрования-дешифрования на 1С

    Reply
  30. tdr1225

    (29) CodeString() в v7icq.dll (Абадонна)

    Reply
  31. vet7777
    Reply
  32. JohnyDeath

    А как, например, расчитать хэш для файла (эксель, дбф и т.п.)?

    Передавать в качестве строки биты данных?

    Reply
  33. JohnyDeath

    Написал вот так:

    Функция ХэшФайла(Знач ИмяФайла) Экспорт
    двФайл = Новый ДвоичныеДанные(ИмяФайла);
    СтрокаФайла = Base64Строка(двФайл);
    Возврат Из_Число_В_16(ХэшБлоками(СтрокаФайла,, 5381, 33));
    КонецФункции //ХэшФайла
    

    Насколько это правильно?

    Reply
  34. laeg

    ХЭШ использую как одностороннего криптования пароля.

    ТС и (31) спасибо за более быстрые алгоритмы.

    Reply
  35. fixin

    да, мне тоже актуально. Делаю подпись документа, просто сцепляя гуиды его реквизитов. Так вот для этих целей как раз и годится хэш. МД5 вычисляется долго или через внешние компоненты. Надо поизучать эту методику, заранее плюсанул…

    Reply
  36. Foxx

    Вот если кому интересно — оптимизированный вариант хэш функции (http://infostart.ru/public/100845/). На строках более килобайта — прирост до 50% по скорости.

    Reply
  37. Serj1C

    Этот день настал! Встречаем 8.3 и встроенный MD5 в платформу! http://downloads.v8.1c.ru/content/Platform/8_3_1_531/1cv8upd.htm

    Reply
  38. slavik27

    Спасибо, очень полезная штука, попробую!

    Reply
  39. alex_4x

    А сталкивался кто нибудь, как в 1С запросе посчитать хэш от полученной строки?

    Цель — получить таблицу с хешем документов, сравнив её с другой такой же таблицей — понять, какие документы отличаются. И всю эту операцию провернуть на сервере.

    Reply
  40. ildarovich

    (40) alex_4x, в статье «Расчет хэш-функции в запросе» описывается один из возможных подходов к решению этой задачи.

    Reply
  41. serg1974

    Напомните пожалуйста значение значение функции % — я вероятно знал — но забыл её, и никак не могу понять смысл выражения hash%TABLE_SIZE

    ну и заодно разъясните — как получено число TABLE_SIZE = 18446744073709551616? Очень интересно!

    я на ассемблере не программировал с 1997 года…

    Спасибо!

    Reply
  42. robix

    Я думал это MD5 А это какой-то непонятный хэш

    Reply
  43. alex_4x

    Я очень извиняюсь, вопрос у меня не совсем (совсем не) по 1С.

    Хочется утилитку, которую можно запускать из командной строки (ну и из 1С) чтобы она быстро считала хеш функции файлов.

    Цель — сопоставление торрентов и уже скачанных файлов. Если есть такая приблуда всё в одном — тоже интересно.

    Проблема в следующем — есть куча скачанных торентами файлов и есть сами файлы торренты — их надо сопоставить.

    В ручную это я сделать вообще не представляю как.

    Извините за частичный оффтоп. Любые ссылки и наводки куда копать — будут очень полезны.

    Reply
  44. silberRus

    (44) Такую написать самому недолго на 1с: Поток + ХешФункция

    и скорость будет норм.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *