Работа со строками: от простого к сложному




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

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

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

<?php // Полная загрузка сервисных книжек, создан 2024-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='\

17 Comments

  1. lunjio

    Упомянули уж тогда, что про сравнение строк в запросе, что когда сравнение идет через оператор = в запросе не важен регистр и пробелы справа отсекаются, когда через подобно тогда важно полное соответствие строк, без учета регистра, другие моменты, по памяти не скажу какие. Так выглядит как напоминалка начинающего программиста.

    Reply
  2. CyberCerber

    Ожидал увидеть что-то про рег выражения и прочее, а 90% статьи — это пересказ СП.

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

    Еще, кстати, «Не ЗначениеЗаполнено» и «ПустаяСтрока» — это разные функции. Хорошо было бы пояснить, когда будет разница

    Reply
  3. Evg-Lylyk

    (1) Поправил описание

    (2) А в чем разница «Не ЗначениеЗаполнено» и «ПустаяСтрока» для строки попробовал… одно и тоже

    Спасибо за отзыв

    Reply
  4. SlavaKron

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

    Reply
  5. CyberCerber

    (3) Да, прошу прощения, на последней версии платформы сейчас проверил, работают одинаково. Раньше была небольшая разница. Можно даже справку почитать, разработчики, видать, ее еще не исправили.

    Reply
  6. Evg-Lylyk

    (4) В функции более читабельно, как еще это делать в 1С. Пользуюсь этими функциями на небольших текстах проблем со скоростью нет.

    Reply
  7. starik-2005

    (2) да, статья без регулярок, конечно, неполна. А в 1С без ограничения ОС регулярки работают крайне ограниченно — только тест шаблона.

    Reply
  8. galich

    Еще надо упомянуть, что функцию конкатенации строк с 8.3.6 лучше делать через СтрСоединить(«Строка 1», Символы.ПС, «Строка 2»). На таком объеме данных выигрыш незначительный, но при больших строках производительность оператора «+» падает катастрофически.

    А в целом, функционал появившийся в 8.3.6 не освещен. Там в платформе появилось много полезных функций (СтрШаблон, СтрРазделить и т.д.), подробнее есть статья в Зазеркалье 1С

    UPD: То ли уже добавил, то ли я невнимательно читаю ))) Информация уже есть в статье.

    Reply
  9. caponid
    Функция ЭтоКорректныйИдентификатор(Строка) Экспорт
    Попытка
    Структура = Новый Структура(Строка);
    Возврат Структура.Свойство(Строка);
    Исключение
    Возврат Ложь;
    КонецПопытки
    КонецФункции
    Reply
  10. independ

    (8) Не знал, проверил на 15000 строк, результат почти в 9 раз быстрее, быстрее только ЗначениеВСтрокуВнутр(ТЗ)-3 сек;

    Выполняется 9 сек

    Для каждого стр из ТЗ Цикл
    МассивСтрок=Новый Массив;
    МассивСтрок.Добавить(стр.Код);
    МассивСтрок.Добавить(стр.Артикул);
    МассивСтрок.Добавить(СтрЗаменить(стр.Наименование,»;»,»»));
    МассивСтрок.Добавить(Формат(стр.Цена,»ЧГ=»));
    МассивСтрок.Добавить(стр.Штрихкод);
    МассивСтрок.Добавить(Формат(стр.SKU,»ЧГ=»));
    МассивСтрок.Добавить(стр.КодАлко);
    МассивСтрок.Добавить(Символы.ПС);
    ФайлТекст=ФайлТекст+СтрСоединить(МассивСтрок,»;»);
    КонецЦикла;
    

    Показать

    Выполняется 80 сек

    Для каждого стр из ТЗ Цикл
    //ФайлТекст=ФайлТекст+стр.Код+»;»+стр.Артикул+»;»+
    //СтрЗаменить(стр.Наименование,»;»,»»)+»;»+
    //Формат(стр.Цена,»ЧГ=»)+»;»+стр.Штрихкод+»;»+
    //Формат(стр.SKU,»ЧГ=»)+»;»+стр.КодАлко+Символы.ПС;
    КонецЦикла; 
    Reply
  11. galich

    (10) Это еще не самое большое замедление (хотя и показательное). Как-то пришлось конкатенировать пару сотен тысяч мелких строк, там разница была в десятки минут.

    Reply
  12. independ

    (11) вышеуказанный пример я делал на 8.3.10.2667 64Бит — MSSQL 2014.

    А к примеру на файловой базе 8.2.19.130 при формировании текстового файла для фронтола — записей примерно 18000, при этом не используется СтрСоединить(), т.к. ее нет, скорость адекватная 8-9 сек,

    Reply
  13. caponid

    (10) Для толстых текстовых данных неплохо подходит ЗаписьXML.ЗаписатьБезОбработки

    Работает очень быстро.

    Reply
  14. starik-2005

    (10) (13)

    … на 15000 строк, результат почти в 9 раз быстрее …

    А запись текста не?

    Для каждого стр из ТЗ Цикл
    ЗаписьТ.ЗаписатьСтроку(стр.Код,»;»);
    ЗаписьТ.ЗаписатьСтроку(стр.Артикул,»;»);
    ЗаписьТ.ЗаписатьСтроку(СтрЗаменить(стр.Наименование,»;»,»»),»;»);
    ЗаписьТ.ЗаписатьСтроку(Формат(стр.Цена,»ЧГ=»),»;»);
    ЗаписьТ.ЗаписатьСтроку(стр.Штрихкод,»;»);
    ЗаписьТ.ЗаписатьСтроку(Формат(стр.SKU,»ЧГ=»),»;»);
    ЗаписьТ.ЗаписатьСтроку(стр.КодАлко);
    КонецЦикла;

    Показать

    А можно поток в памяти для скорости еще к записи текста привязать. Думаю будет быстрее.

    Reply
  15. kembrik

    Ковычки заменить на Кавычки и совсем хорошо будет

    Reply
  16. Evg-Lylyk

    (15) Спс. Поправил

    Reply
  17. German_Tagil

    хорошо как раз сегодня искал

    задача примерно такая

    есть строки «АЕШ 8888-4007 Разработка»

    «АЕШ 8888-4007 Разработка проекта»

    как правильно определить вхождение 2 пробела

    и поделить строки на левую и правую

    Reply

Leave a Comment

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