Универсальные функции с примерами использования
Программирование - Универсальные функции
Замер времени вхождение в интервал разбить строку парсинг строки
Вместо предисловия
Я знаю про CloudConf, и сервисы хранения кода, но на Инфостарте сижу каждый день, поэтому мне здесь удобнее. Да и возможность поделиться какими-то своими идеями, получив отклик, тоже интересна.
Код я оформляю в соответствии с соглашениями о написании кода.
Для более точных замеров - с версии платформы 8.2.17 можно использовать метод ГК ТекущаяУниверсальнаяДатаВМиллисекундах()
Старт = ТекущаяДата();
// Произвольный код....
Финиш = ТекущаяДата();
КолСекундЛог = Финиш - Старт;
чч = ЦЕЛ(КолСекундЛог/3600);
мм = ЦЕЛ((КолСекундЛог - чч*3600)/60);
сс = КолСекундЛог - чч*3600 - мм*60;
СтрокаВремя = Формат(чч, "ЧЦ=2; ЧН=; ЧВН=") + ":" +
Формат(мм, "ЧЦ=2; ЧН=; ЧВН=") + ":" +
Формат(сс, "ЧЦ=2; ЧН=; ЧВН=") + " ("+ КолСекундЛог +" сек.)";
Сообщить("" + ТекущаяДата() + ": обработано за " + СтрокаВремя + ".", СтатусСообщения.Информация);
// Проверка значения на вхождение в некоторый интервал
//
//Параметры (название, тип, дифференцированное значение):
// Значение - Число, Дата - Проверяемое значение
// НачалоИнтервала - Число, Дата
// КонецИнтервала - Число, Дата
// СтрогаяПроверка - Булево
//
//Возвращаемое значение:
// Булево
//
Функция ЗначениеВИнтервале(Значение, НачалоИнтервала, КонецИнтервала, СтрогаяПроверка = Ложь)
Если СтрогаяПроверка Тогда
Возврат НачалоИнтервала < Значение И Значение < КонецИнтервала;
Иначе
Возврат НачалоИнтервала <= Значение И Значение <= КонецИнтервала;
КонецЕсли;
КонецФункции
Пример использования:
//.....
Если НЕ ЗначениеВИнтервале(ДатаПоказателя, НачалоПериода, КонецПериода) Тогда
Продолжить;
КонецЕсли;
//...
// Универсальная функция для парсинга строк
//
//Параметры:
// ИсходнаяСтрока - Строка
// СтрокаРазделитель - Строка
// ОбрезатьПробелы - Булево
//
//Возвращаемое значение:
// СтруктураЧастиСтроки - Структура
// * Левая - Строка - Левая часть строки (до начала СтрокаРазделитель)
// * Правая - Строка - Правая часть строки (после окончания СтрокаРазделитель)
// * Разбито - Булево - флаг указывающий что в исходной строке есть СтрокаРазделитель
//
Функция РазбитьСтроку(ИсходнаяСтрока, СтрокаРазделитель, ОбрезатьПробелы = Истина)
поз = Найти(ИсходнаяСтрока, СтрокаРазделитель);
ДлинаРазделителя = СтрДлина(СтрокаРазделитель);
Разбито = поз > 0;
ЛеваяЧастьСтроки = Лев(ИсходнаяСтрока, поз - 1);
Если ОбрезатьПробелы Тогда
ЛеваяЧастьСтроки = СокрЛП(ЛеваяЧастьСтроки);
КонецЕсли;
ПраваяЧастьСтроки = Сред(ИсходнаяСтрока, поз + ДлинаРазделителя);
Если ОбрезатьПробелы Тогда
ПраваяЧастьСтроки = СокрЛП(ПраваяЧастьСтроки);
КонецЕсли;
СтруктураЧастиСтроки = Новый Структура("Левая, Правая, Разбито"
, ЛеваяЧастьСтроки
, ПраваяЧастьСтроки
, Разбито);
Возврат СтруктураЧастиСтроки;
КонецФункции
Пример использования:
//Парсинг строки вида "дд.мм.гггг":
Результат = Дата(1,1,1);
СтруктураЧастиСтроки = РазбитьСтроку(ЗначениеЯчейки, ".");
Если СтруктураЧастиСтроки.Разбито Тогда
ст_дд = Прав(СтруктураЧастиСтроки.Левая, 2);
чс_дд = Число(ст_дд);
СтруктураЧастиСтроки = РазбитьСтроку(СтруктураЧастиСтроки.Правая, ".");
Если СтруктураЧастиСтроки.Разбито Тогда
ст_мм = Прав(СтруктураЧастиСтроки.Левая, 2);
чс_мм = Число(ст_мм);
ст_гггг = Лев(СтруктураЧастиСтроки.Правая, 4);
чс_гггг = Число(ст_гггг);
Результат = Дата(чс_гггг, чс_мм, чс_дд);
КонецЕсли;
КонецЕсли;
Возврат Результат;
В комментарии ниже дан более логичный код, с использованием построителя для этих целей (к сожалению, не пойму сейчас как ссылку на коммент дать). Оставил здесь эту функцию потому что мне иногда нужен перебор табличного документа, и ее использую качестве заготовки.
// Преобразование табличного документа в таблицу значений
//
//Параметры:
// ТабДок - ТабличныйДокумент
//
//Возвращаемое значение:
// ТаблицаЗначений
//
Функция ТабличныйДокументВТаблицуЗначений(ТабДок)
ТаблицаДанныеДокумента = Новый ТаблицаЗначений();
Для СчетчикКолонок = 1 По ТабДок.ШиринаТаблицы Цикл
ИмяКолонки = "_" + СчетчикКолонок;
ТаблицаДанныеДокумента.Колонки.Добавить(ИмяКолонки);
Для СчетчикСтрок = 1 По ТабДок.ВысотаТаблицы Цикл
ИндексСтроки = СчетчикСтрок - 1;
Если СчетчикКолонок = 1 Тогда
СтрокаТаблицы = ТаблицаДанныеДокумента.Добавить();
Иначе
СтрокаТаблицы = ТаблицаДанныеДокумента[ИндексСтроки];
КонецЕсли;
ТекущаяЯчейка = ТабДок.Область(СчетчикСтрок, СчетчикКолонок);
ЗначениеЯчейки = ТекущаяЯчейка.Текст;
СтрокаТаблицы[ИмяКолонки] = ЗначениеЯчейки;
КонецЦикла
КонецЦикла;
Возврат ТаблицаДанныеДокумента;
КонецФункции
Решение задачи чтения иерархии табличного документа. Здесь стоит упомянуть статью Чтение группировок табличного документа:
- Сам алгоритм статьи не отрабатывает корректно, если есть несколько вложенных уровней группировок. Его исправили в последнем комментарии, но автор статью почему-то обновлять не стал.
- Предложенная там реализация отличается плохо читабельным кодом. Кроме этого задействовано три метода, в моем подходе - один. Подход к решению тоже разный: я использую видимость области строки, там используется парсинг таб. дока через документ DOM.
- При этом по скорости оба алгоритма показывают примерно одинаковое время
// Дерево значений по табличному документу, с аналогичной иерархией существующих группировок
//
//Параметры:
// ТабДок - ТабличныйДокумент
// СвойстваКолонок - ТаблицаЗначений
// *Имя - Строка - Имя колонки дерева, в которую будут помещены данные области колонки табличного документа
// *Номер - Число - Номер колонки в табличном документе
// ВысотаШапки - Число - для отсечения незначащих строк
// УдалятьФлаговыеКолонки - Булево - При анализе табличного документа в дерево добавляются служебные колонки
// "Начало" и "Конец", где сохраняются диапазоны группировки. Если Истина - то эти колонки при окончании
// алгоритма будут удалены.
// ИсключатьПустыеСтроки - Булево - Если Истина, то строки в которых значения всех колонок пустые будут исключены
//
//Возвращаемое значение:
// ДеревоЗначений
//
Функция ТабличныйДокументВДеревоЗначений(ТабДок, СвойстваКолонок, ВысотаШапки = 0, УдалятьФлаговыеКолонки = Истина, ИсключатьПустыеСтроки = Истина)
ДеревоДокумента = Новый ДеревоЗначений;
Для каждого ОписаниеКолонки Из СвойстваКолонок Цикл
ДеревоДокумента.Колонки.Добавить(ОписаниеКолонки.Имя);
КонецЦикла;
ДеревоДокумента.Колонки.Добавить("Начало");
ДеревоДокумента.Колонки.Добавить("Конец");
Кэш = Новый ТабличныйДокумент;
Кэш.Вывести(ТабДок);
Для сч = -Кэш.КоличествоУровнейГруппировокСтрок() По 0 Цикл
Кэш.ПоказатьУровеньГруппировокСтрок(-сч);
КонецЦикла;
ПройденныеСтроки = Новый Соответствие;
ПройденныеУровни = Новый Соответствие;
ПустыеСтроки = Новый Соответствие;
КоличествоУровней =Кэш.КоличествоУровнейГруппировокСтрок();
Для СчетчикУровня=1 По КоличествоУровней Цикл
ПройденныеУровни.Вставить(СчетчикУровня, Новый Массив);
Кэш.ПоказатьУровеньГруппировокСтрок(СчетчикУровня-1);
Для СчетчикСтрок=1 По Кэш.ВысотаТаблицы Цикл
Если СчетчикСтрок <= ВысотаШапки Тогда
Продолжить;
КонецЕсли;
Если ПройденныеСтроки.Получить(СчетчикСтрок) = Истина Тогда
Продолжить;
КонецЕсли;
Если ИсключатьПустыеСтроки Тогда
ЭтоПустаяСтрока = ПустыеСтроки.Получить(СчетчикСтрок);
Если ЭтоПустаяСтрока = Неопределено Тогда
ЭтоПустаяСтрока = Истина;
Для каждого ОписаниеКолонки Из СвойстваКолонок Цикл
ОбластьЯчейки = Кэш.Область(СчетчикСтрок, ОписаниеКолонки.Номер);
Если ЗначениеЗаполнено(ОбластьЯчейки.Текст) Тогда
ЭтоПустаяСтрока = Ложь;
Прервать;
КонецЕсли;
КонецЦикла;
ПустыеСтроки.Вставить(СчетчикСтрок, ЭтоПустаяСтрока);
КонецЕсли;
Если ЭтоПустаяСтрока Тогда
Продолжить;
КонецЕсли;
КонецЕсли;
ИмяОбласти = "R" + Формат(СчетчикСтрок, "ЧГ=");
ОбластьСтроки = Кэш.Область(ИмяОбласти);
Если ОбластьСтроки.Видимость Тогда
Если СчетчикУровня = 1 Тогда
Родитель = ДеревоДокумента;
Иначе
МассивНомеровСтрокПредУровня = ПройденныеУровни.Получить(СчетчикУровня-1);
Для сч=-МассивНомеровСтрокПредУровня.ВГраница() По 0 Цикл
НомерСтрокиРодителя = МассивНомеровСтрокПредУровня[-сч];
Если НомерСтрокиРодителя < СчетчикСтрок Тогда
Прервать;
КонецЕсли;
КонецЦикла;
Родитель = ДеревоДокумента.Строки.Найти(НомерСтрокиРодителя, "Начало", Истина);
КонецЕсли;
Узел = Родитель.Строки.Добавить();
Узел.Начало = СчетчикСтрок;
Для каждого ОписаниеКолонки Из СвойстваКолонок Цикл
ОбластьЯчейки = Кэш.Область(СчетчикСтрок, ОписаниеКолонки.Номер);
Узел[ОписаниеКолонки.Имя] = СокрЛП(ОбластьЯчейки.Текст);
КонецЦикла;
Если СчетчикУровня < КоличествоУровней Тогда
ПройденныеСтроки.Вставить(СчетчикСтрок, Истина);
МассивНомеровСтрокУровня = ПройденныеУровни.Получить(СчетчикУровня);
МассивНомеровСтрокУровня.Добавить(СчетчикСтрок);
КонецЕсли;
КонецЕсли;
Если Узел <> Неопределено Тогда
Узел.Конец = СчетчикСтрок;
КонецЕсли;
КонецЦикла;
КонецЦикла;
Если УдалятьФлаговыеКолонки Тогда
ДеревоДокумента.Колонки.Удалить("Начало");
ДеревоДокумента.Колонки.Удалить("Конец");
КонецЕсли;
Возврат ДеревоДокумента;
КонецФункции
Приведенный ниже код иллюстрирует работу с файлом типа "xlsx". Работа ведется из управляемой формы:
-
пользователь выбирает файл;
-
файл помещается в временное хранилище, и становится доступен на сервере в виде двоичных данных;
-
производится запись двоичных данных в временный файл на сервере, с проверкой: если запись временного файла занимает более 30 секунд, пользователь получит уведомление о прерывании загрузки.
// Выбор загружаемого файла, заполнение служебного реквизита формы "АдресФайлаВВременномХранилище"
//
//Возвращаемое значение:
// Булево - Истина при успешном выборе файла
//
&НаКлиенте
Функция ПроизведенУспешныйВыборФайла()
ЭтаФорма.АдресФайлаВВременномХранилище = "";
#Если ВебКлиент Тогда
Если НЕ ПодключитьРасширениеРаботыСФайлами() Тогда
УстановитьРасширениеРаботыСФайлами();
ПодключитьРасширениеРаботыСФайлами();
КонецЕсли;
ПоместитьФайл(ЭтаФорма.АдресФайлаВВременномХранилище);
#Иначе
ДиалогФыбораФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
ДиалогФыбораФайла.Фильтр = "Таблица(*.xlsx)|*.xlsx";
ДиалогФыбораФайла.Заголовок = "Выберите файл";
ДиалогФыбораФайла.ПредварительныйПросмотр = Ложь;
ДиалогФыбораФайла.Расширение = "xlsx";
ДиалогФыбораФайла.МножественныйВыбор = Ложь;
Если ДиалогФыбораФайла.Выбрать() Тогда
ФайлЗагрузки = ДиалогФыбораФайла.ПолноеИмяФайла;
Иначе
Возврат Ложь;
КонецЕсли;
ЭтаФорма.АдресФайлаВВременномХранилище = ПоместитьВоВременноеХранилище(Новый ДвоичныеДанные(ДиалогФыбораФайла.ПолноеИмяФайла));
#КонецЕсли
Возврат НЕ ПустаяСтрока(ЭтаФорма.АдресФайлаВВременномХранилище);
КонецФункции
Пример использования:
//...
&НаКлиенте
Процедура ЗагрузитьФайл(Команда)
Если ПроизведенУспешныйВыборФайла() Тогда
ВыполнитьЗагрузкуФайлаНаСервере();
КонецЕсли;
КонецПроцедуры
//...
&НаСервере
Процедура ВыполнитьЗагрузкуФайлаНаСервере()
АдресВременногоФайла = ПолучитьИмяВременногоФайла("xlsx");
// Запись двоичных данных из вр. хранилища:
ДвДанные = ПолучитьИзВременногоХранилища(ЭтаФорма.АдресФайлаВВременномХранилище);
Если ЗаписьФайлаПроизошлаУспешно(ДвДанные, АдресВременногоФайла) Тогда // см. ниже "Проверка записи файла
// обработка данных файла...
Иначе
ЛОГ("загрузка прервана на сервере - файл """+ АдресВременногоФайла +""" не обнаружен...");
КонецЕсли;
КонецПроцедуры
//...
// Попытка записи данных в файл с таймаутом на время записи
//
//Параметры:
// Источник - Произвольный объект 1С, поддерживающий запись в файл через метод "Записать" - например ДвоичныеДанные или ТекстовыйДокумент
// АдресФайла - Строка
// ОграничениеВремениЗаписи - Число, количество секунд в течение которого будет выполнятся проверка существования файла
// КомандаЗаписи - Строка, для различных случаев сохранения файла, в алгоритме будет параметром метода "Выполнить"
//
//Возвращаемое значение:
// Булево
//
Функция ЗаписьФайлаПроизошлаУспешно(Источник, АдресФайла, ОграничениеВремениЗаписи = 30, КомандаЗаписи = "")
Попытка
Если ПустаяСтрока(КомандаЗаписи) Тогда
Источник.Записать(АдресФайла);
Иначе
Выполнить(КомандаЗаписи);
КонецЕсли;
ТекДата = ТекущаяДата();
ПредСекунда = 0;
Пока Истина Цикл
КолСекунд = ТекущаяДата() - ТекДата;
ТекСекунда = КолСекунд;
Если ПредСекунда < ТекСекунда Тогда
// Раз в секунду - проверка существования записываемого файла
ЗаписываемыйФайл = Новый Файл(АдресФайла);
Если ЗаписываемыйФайл.Существует() Тогда
Возврат Истина;
КонецЕсли;
ПредСекунда = ТекСекунда;
КонецЕсли;
Если КолСекунд > ОграничениеВремениЗаписи Тогда
ВызватьИсключение "время записи файла превысило "+ ОграничениеВремениЗаписи +" секунд";
КонецЕсли;
КонецЦикла;
Исключение
Лог("неудачная попытка записи файла на сервере - " + ОписаниеОшибки());
Возврат Ложь;
КонецПопытки;
КонецФункции
Пример использования:
//...
ТекстXSD = ЭтотОбъект.ПолучитьМакет("МакетXSD");
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xsd");
// Запись xsd файла с проверкой по тайм-ауту
Если НЕ ЗаписьФайлаПроизошлаУспешно(ТекстXSD, ИмяВременногоФайла, 5) Тогда
Возврат;
КонецЕсли;
// Или, если нужна запись с параметром:
Если НЕ ЗаписьФайлаПроизошлаУспешно(ТекстXSD, ИмяВременногоФайла, 5, "Источник.Записать(АдресФайла, КодировкаТекста.UTF8);") Тогда
Возврат;
КонецЕсли;
//...
// Проверка доступности переданного узла с помощью команды ping
//
//Параметры:
// АдресURL - пингуемый адрес
//
//Возвращаемое значение:
// Булево
//
Функция Пинг(АдресURL = "")
Если ПустаяСтрока(АдресURL) Тогда
АдресURL = "www.ya.ru";
КонецЕсли;
objShell = Новый COMОбъект("WScript.Shell") ;
objScriptExec = objShell.Exec("ping.exe -n 1 " + АдресURL);
strPingResults = НРег(objScriptExec.StdOut.ReadAll());
ЕстьСоединение = Найти(strPingResults, "ttl=") > 0;
Возврат ЕстьСоединение;
КонецФункции
Пример использования:
//...
ЕстьСоединениеСИнтернетом = Пинг(); // По-умолчанию пингуется www.ya.ru
//...
// Упрощенный конструктор описания типа
//
//Параметры:
// ИмяТипа - Строка, Массив элементов типа "Тип" - первый параметр конструктора ОписаниеТипов()
// п1 - Произвольный, первый параметр квалификатора
// п2 - Произвольный, второй параметр квалификатора
// п3 - Произвольный, третий параметр квалификатора
//
//Возвращаемое значение:
// ОписаниеТипов
//
Функция ОписаниеТипа(ИмяТипа, п1 = Неопределено, п2 = Неопределено, п3 = Неопределено)
Перем Результат;
Если ИмяТипа = "Строка" Тогда
п1 = ?(п1 = Неопределено, 0, п1);
Если п2 = Неопределено Тогда
п2 = ДопустимаяДлина.Переменная;
КонецЕсли;
КвалСтроки = Новый КвалификаторыСтроки(п1, п2);
Результат = Новый ОписаниеТипов(ИмяТипа,,,,КвалСтроки);
ИначеЕсли ИмяТипа = "Число" Тогда
п1 = ?(п1 = Неопределено, 0, п1);
п2 = ?(п2 = Неопределено, 0, п2);
Если п3 = Неопределено Тогда
п3 = ДопустимыйЗнак.Любой;
КонецЕсли;
КвалЧисла = Новый КвалификаторыЧисла(п1, п2, п3);
Результат = Новый ОписаниеТипов(ИмяТипа,,,КвалЧисла);
ИначеЕсли ИмяТипа = "Дата" Тогда
Если п1 = Неопределено Тогда
п1 = ЧастиДаты.ДатаВремя;
КонецЕсли;
КвалДаты = Новый КвалификаторыДаты(п1);
Результат = Новый ОписаниеТипов(ИмяТипа,,,,,КвалДаты);
Иначе
Результат = Новый ОписаниеТипов(ИмяТипа);
КонецЕсли;
Возврат Результат;
КонецФункции
Пример использования:
//...
Функция НоваяТаблицаОписанияОпераций()
тз = Новый ТаблицаЗначений;
тз.Колонки.Добавить("Сумма" , ОписаниеТипа("Число", 15, 2));
тз.Колонки.Вставить("Описание" , ОписаниеТипа("Строка", 100));
тз.Колонки.Вставить("ДатаОперации" , ОписаниеТипа("Дата"));
МассивТипов = Новый Массив();
МассивТипов.Добавить(Тип("ДокументСсылка.Встреча"));
МассивТипов.Добавить(Тип("ДокументСсылка.ЗапланированноеВзаимодействие"));
тз.Колонки.Вставить("Событие" , ОписаниеТипа(МассивТипов));
тз.Колонки.Вставить("Ответственный" , ОписаниеТипа("СправочникСсылка.Пользователи"));
Возврат тз;
КонецФункции
//...
ADODBConnection = Новый COMОбъект("ADODB.Connection");
ADODBConnection.Provider = "Microsoft.ACE.OLEDB.12.0";
ADODBConnection.Properties("Data Source").Value = СокрЛП(ЭтотОбъект.ПутьКФайлу);
ADODBConnection.Properties("Extended Properties").Value = "Excel 12.0;HDR=Yes;IMEX=1";
ADODBConnection.Open();
// Получить список имен листов:
// ADODBRecordset = Новый COMОбъект("ADODB.Recordset");
// ADODBRecordset = ADODBConnection.OpenSchema(20);
// СписокЛистов = Новый СписокЗначений;
// Пока НЕ ADODBRecordset.EOF Цикл
// ИмяЛиста = ADODBRecordset.Fields("TABLE_NAME").Value;
// Если Найти(ИмяЛиста, "_xlnm#_FilterDatabase") = 0 Тогда
// СписокЛистов.Добавить(ИмяЛиста);
// КонецЕсли;
// ADODBRecordset.MoveNext();
// КонецЦикла;
// ADODBRecordset.Close();
// СписокЛистов.СортироватьПоЗначению(НаправлениеСортировки.Убыв);
// ИмяЛиста = СписокЛистов[0].Значение;
ТекстЗапроса = "SELECT * FROM [" + ЭтотОбъект.ИмяЛиста + "$]";
ADODBRecordset = Новый COMОбъект("ADODB.Recordset");
ADODBRecordset.Open(ТекстЗапроса, ADODBConnection);
КолвоКолонокExcel = ADODBRecordset.Fields.Count;
СчетчикСтрок = 1;
Пока ADODBRecordset.EOF() = 0 Цикл
Для СчетчикКолонок = 1 ПО КолвоКолонокExcel Цикл
Поле = ADODBRecordset.Fields.Item(СчетчикКолонок - 1);
Если Поле.ActualSize = 0 Тогда// Пустое поле EXCEL.
Продолжить;
КонецЕсли;
ЗначениеЯчейки = Поле.Value;
// Обработка значения ячейки
КонецЦикла;
ADODBRecordset.MoveNext(); // Следующая строка.
СчетчикСтрок = СчетчикСтрок + 1;
КонецЦикла;
ADODBConnection.Close();
ADODBRecordset = Неопределено;
ADODBConnection = Неопределено;
Этот метод служит для решения частной задачи отображения группировок колонок в табличных документах, полученных с помощью СКД. Исходная идея вот здесь: СКД. Как объединить заголовки родительских группировок колонок в таблице, я лишь причесал код. Этот вариант не будет работать с уже объединенными ячейками. Может позже допилю, пока нет нужды. Как использовать: в СКД делаем одну группировку колонок, в которую добавлем необходимые поля. После вывода табличного документа на форму вызываем метод.
// Объединяет ячейки шапки табличного документа с повторяющимся текстом
// Служит для решения задачи отображения группировок колонок в табличных документах, полученных с помощью СКД
//
//Параметры:
// ТабДок - ТабличныйДокумент
// ВысотаШапки - Число, если не передана, высотой шапки считается высота фиксации таблицы
//
Процедура СвернутьЗаголовкиШапкиТабличногоДокумента(ТабДок, ВысотаШапки = 0)
ВысотаШапки = ?(ВысотаШапки = 0, ТабДок.ФиксацияСверху, ВысотаШапки);
НачалоШапки = ?(ТабДок.ФиксацияСлева = 0, 1, ТабДок.ФиксацияСлева);
ЭтоПервоеОбъединение = Истина;
Для СчетчикСтрок=1 По ВысотаШапки Цикл
НомерПервойКолонкиОбъединения = 0;
Для СчетчикКолонок=НачалоШапки По ТабДок.ШиринаТаблицы Цикл
ОбъединятьЯчейки = Ложь;
Ячейка = ТабДок.Область(СчетчикСтрок, СчетчикКолонок);
Если ПустаяСтрока(Ячейка.Текст) Тогда
Продолжить;
КонецЕсли;
ЯчейкаСлед = ТабДок.Область(СчетчикСтрок, СчетчикКолонок+1);
ОбъединятьЯчейки = Ячейка.Текст = ЯчейкаСлед.Текст;
Если ОбъединятьЯчейки Тогда
НомерПервойКолонкиОбъединения = ?(НомерПервойКолонкиОбъединения = 0, СчетчикКолонок, НомерПервойКолонкиОбъединения);
ИначеЕсли НомерПервойКолонкиОбъединения > 0 Тогда
ТекстЗаголовка = ТабДок.Область(СчетчикСтрок, СчетчикКолонок).Текст;
ОбъединяемаяОбласть = ТабДок.Область(СчетчикСтрок, НомерПервойКолонкиОбъединения, СчетчикСтрок, СчетчикКолонок);
ОбъединяемаяОбласть.Объединить();
ОбъединяемаяОбласть.ГоризонтальноеПоложение = ГоризонтальноеПоложение.Центр;
ОбъединяемаяОбласть.Текст = ТекстЗаголовка;
НомерПервойКолонкиОбъединения = 0;
КонецЕсли;
КонецЦикла;
КонецЦикла;
КонецПроцедуры
// Формирование идентификатора по правилам образования имен переменных 1С по входящей строке
// Пример "Статья возмещение НДС" => "СтатьяВозмещениеНДС"
//
//Параметры:
// стр - Строка - от которой необходимо получить идентификатор
//
//Возвращаемое значение:
// Строка
//
Функция ИдентификаторПоСтроке(ЗНАЧ стр)
ДопустимыеСимволы = "ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮЁQWERTYUIOPASDFGHJKLZXCVBNM" +
"йцукенгшщзхъфывапролджэячсмитьбюёqwertyuiopasdfghjklzxcvbnm_ ";
СимволыЦифр = "0987654321";
ДопустимыеСимволы = СимволыЦифр + ДопустимыеСимволы;
Пока Найти(стр, " ") > 0 Цикл
стр = СтрЗаменить(стр, " ", " ");
КонецЦикла;
ДлинаСтроки = СтрДлина(стр);
СледующаяЗаглавная = Истина;
Идентификатор = "";
Для сч=1 По ДлинаСтроки Цикл
сим = Сред(стр, сч, 1);
Если Найти(ДопустимыеСимволы, сим) = 0 Тогда
сим = "_";
КонецЕсли;
Если сч = 1 И сим = "_" Тогда
Продолжить;
КонецЕсли;
Если сим = " " Тогда
СледующаяЗаглавная = Истина;
Продолжить;
КонецЕсли;
Если СледующаяЗаглавная Тогда
СледующаяЗаглавная = Ложь;
Сим = Врег(Сим);
КонецЕсли;
ЭтоЦифра = Найти(СимволыЦифр, сим) > 0;
Если сч = 1 И ЭтоЦифра Тогда
сим = "_" + сим;
КонецЕсли;
Если ЭтоЦифра Тогда
СледующаяЗаглавная = Истина;
КонецЕсли;
Идентификатор = Идентификатор + Сим;
КонецЦикла;
Возврат Идентификатор;
КонецФункции
// Формирование структуры для первой (единственной) записи результата запроса
//
//Параметры:
// РезультатЗапроса - РезультатЗапроса, источник данных
//
//Возвращаемое значение:
// Структура - Набор ключей повторяет набор колонок результат запроса, значения заполняются
// значениями первой записи выборки
//
Функция РезультатЗапросаВСтруктуру(РезультатЗапроса) Экспорт
СтруктураРезультат = Новый Структура;
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
Для каждого Колонка Из РезультатЗапроса.Колонки Цикл
Если Выборка.Количество() = 0 Тогда
МассивТипов = Колонка.ТипЗначения.Типы();
ОбщегоНазначенияКлиентСервер.УдалитьЗначениеИзМассива(МассивТипов, Тип("Null"));
ОписаниеТиповБезNULL = Новый ОписаниеТипов(МассивТипов);
ЗначениеПоля = ОписаниеТиповБезNULL.ПривестиЗначение();
Иначе
ЗначениеПоля = Выборка[Колонка.Имя];
КонецЕсли;
СтруктураРезультат.Вставить(Колонка.Имя, ЗначениеПоля);
КонецЦикла;
Возврат СтруктураРезультат;
КонецФункции
// Проверка входящей строки на соответствие паттерну GUID: проверяется что строка состоит из 4 блоков с разделитетем "-"
// с разрядностью 8-4-4-4 соответственно. Замечено, что длина последнего блока может меняться, поэтому он не проверяется.
//
//Параметры:
// Идентификатор - Строка
//
//Возвращаемое значение:
// Булево
//
Функция ЭтоGUID(Знач Идентификатор)
Результат = Ложь;
ДлиныБлоков = Новый Соответствие;
ДлиныБлоков.Вставить(1, 8);
ДлиныБлоков.Вставить(2, 4);
ДлиныБлоков.Вставить(3, 4);
ДлиныБлоков.Вставить(4, 4);
Для сч=1 По 4 Цикл
поз = Найти(Идентификатор, "-");
Если поз = 0 Тогда
Прервать;
КонецЕсли;
Блок = Сред(Идентификатор, 1, поз-1);
ДлинаБлока = ДлиныБлоков.Получить(сч);
Если СтрДлина(Блок) <> ДлинаБлока Тогда
Прервать;
КонецЕсли;
Идентификатор = Сред(Идентификатор, поз+1);
Если сч = 4 Тогда
Результат = Истина;
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
См. также
Специальные предложения
(2) tireal, спасибо за комментарий. Но:
- свою функцию я могу использовать в любом релизе платформы. Да, я порой работаю с 8.1 - как ни странно оно еще есть.
- если мне когда-нибудь понадобится настолько низкоуровневое отслеживание выполнения алгоритма - я скорее воспользуюсь замером времени отладчика
- могу предположить что назначение использования вашей функции - тестирование скорости исполнения алгоритма (т. е. она для этой цели писалась); моей - просто замер времени работы для сообщения/ помещения в лог - секунды вполне устраивают
Останусь при своем, но буду знать и о таком варианте, еще раз спасибо.
Попытка
Scr = Новый COMОбъект("MSScriptControl.ScriptControl");
Исключение
Сообщить(ОписаниеОшибки(), СтатусСообщения.Внимание);
Возврат 0;
КонецПопытки;
Scr.Language = "javascript";
Время = Scr.Eval("new Date().getTime()");
Возврат Время;
ПоказатьПо поводу разделения строки - держи в копилку:
Функция ИзвлечьСлово(Строка, Разделитель)
П = найти(Строка+Разделитель, Разделитель);
Ответ = Лев(Строка, П-1);
Строка = Сред(Строка, П+СтрДлина(Разделитель));
Возврат Ответ;
КонецФункции
Пример:
стр = "1,2,3,4";
Сообщить(ИзвлечьСлово(стр, ",")); //1
Сообщить(стр); // 2,3,4
Сообщить(ИзвлечьСлово(стр, ",")); //2
Сообщить(стр); // 3,4
Сообщить(ИзвлечьСлово(стр, ",")); //3
Сообщить(стр); // 4
Сообщить(ИзвлечьСлово(стр, ",")); //4
Сообщить(стр); // "" (пустая строка)
Сообщить(ИзвлечьСлово(стр, ",")); // "" (пустая строка)
ПоказатьЕсли заранее известно, что строка не содержит символов перевода строки, то можно пользоваться стандартным:
СтрПолучитьСтроку(СтрЗаменить(<ИсходнаяСтрока>, <Разделитель>, Символы.ПС), <НомерСлова>)
(7) h00k, почитайте внимательнее мой комментарий в (3) . И да, мне удобнее воспользоваться замером времени, чем вставлять в конфу отладочную печать. Не могу вспомнить случая, когда я не мог без этого обойтись. Может опыта еще маловато)
Имхо, все извращения с получением настолько точного времени - связаны с необходимостью отслеживать СКОРОСТЬ, а не ВРЕМЯ выполнения алгоритма. Разница трудноуловима, но она есть. Мне не надо с точностью до миллисекунды знать сколько времени проводился массив документов - 20 мин меня вполне устроит. Но если я тестирую напр. как быстрее создавать таблицу значений - тут нужны миллисекунды. Просто для универсальности функция замера обычно реализуется наиболее детализированно - т.е. через тот-же ява-скрипт. Ну а мне как-то не нужно было. Если понадобится - возьму из (6).
Смешно. Вы думаете всякие извращения с джаваскриптами и т.п. появились потому что ни кто не додумался время выполнения в отладчике смотреть?!
Спасибо любимой 1С, они хоть от необходимости использовать внешние скрипты избавили, добавив в платформу функцию
ТекущаяУниверсальнаяДатаВМиллисекундах()
А для старых версий платформы остается только замерять время выполнения при помощи внешних средств, например джава-скрипта.
докВремяНачала = ТекущаяУниверсальнаяДатаВМиллисекундах();
//процедура замера
докВремяКонцаВыполнения = ТекущаяУниверсальнаяДатаВМиллисекундах();
докДельта = (докВремяКонцаВыполнения - докВремяНачала) / 1000;
докДельтаПредставление = ПолучитьПредставлениеВремени(докДельта);
Функция ПолучитьПредставлениеВремени(Время)
Секунды = Время % 60;
Минуты = (Время-Секунды)/60;
Минуты = Минуты % 60;
Часы = (Время-Секунды-Минуты)/3600;
Возврат "" + Окр(Часы) + " ч. " + Окр(Минуты) + " мин. " + Окр(Секунды, 2) + " сек.";
КонецФункции
ПоказатьВы можете ошибаться... собственное развитие и вынужденная работа со старыми версиями конфигураций в учетных системах заказчика/ работодателя - в большинстве случаев ни как не связаны.
Переход предприятия на новую систему учета/ платформу - это достаточно дорогое удовольствие, и только из-за желания программиста работать с новыми версиями платформ/ конфигураций ни кто, в здравом уме, переходить не будет.
Вот это вроде с давних времен работает
Построитель = Новый ПостроительОтчета;
Построитель.ИсточникДанных=Новый ОписаниеИсточникаДанных(ТабДок.Область());
Построитель.Выполнить();
ТЗ = Построитель.Результат.Выгрузить();
// клиентский модуль
Процедура ЗасечьТаймер() Экспорт
УправляемыйИнтерфейсСервер.СохранитьЗначениеВХранилище( "ТекВремяТаймера", ТекущаяДата() );
//ПоместитьВоВременноеХранилище( ТекущаяДата(), );
КонецПроцедуры
Процедура ПоказатьВремяТаймера( пЗаголовок = "", пЧерезСообщение = Ложь ) Экспорт
д = УправляемыйИнтерфейсСервер.ПолучитьЗначениеИзХранилища( "ТекВремяТаймера" );
общ = ТекущаяДата() - д;
мин = цел( общ / 60 );
сек = общ - мин * 60;
час = цел( мин / 60);
мин = мин - час * 60;
лВрем = ?( пЧерезСообщение, "Время выполнения:", "Время:" );
Если час <> 0 Тогда
лВрем = лВрем + " " + час + " час"
КонецЕсли;
Если Мин <> 0 Тогда
лВрем = лВрем + " " + мин + " мин"
КонецЕсли;
лВрем = лВрем + " " + сек + " сек";
Если пЧерезСообщение=Ложь Тогда
ПоказатьОповещениеПользователя( пЗаголовок, , лВрем );
Иначе
Сообщить( ?( ПустаяСтрока( пЗаголовок ), "Действие выполнено", пЗаголовок ) + ". " + лВрем );
КонецЕсли;
КонецПроцедуры
// серверный модуль УправляемыйИнтерфейсСервер
Процедура СохранитьЗначениеВХранилище( пКлюч, пЗначение ) Экспорт
ХранилищеСистемныхНастроек.Сохранить( , пКлюч, пЗначение, , ПараметрыСеанса.ТекущийПользователь );
КонецПроцедуры
Функция ПолучитьЗначениеИзХранилища( пКлюч ) Экспорт
Возврат ХранилищеСистемныхНастроек.Загрузить( , пКлюч, , ПараметрыСеанса.ТекущийПользователь );
КонецФункции
// пример использования
&НаКлиенте
Процедура СделатьЧтоТо()
УправляемыйИнтерфейс.ЗасечьТаймер();
// что-то делаем
// ......................
УправляемыйИнтерфейс.ПоказатьВремяТаймера("Сделали что-то!");
КонецПроцедуры
ПоказатьЕще раз прочитайте эту часть
Мне кажется вы не правильно поняли блок как не надо делать

Просмотры 30725
Загрузки 0
Комментарии 36
Создание 26.02.16 01:55
Обновление 23.07.18 23:05
№ Публикации 449681
Рубрики Универсальные функции
Кому Программист
Тип файла Нет файла
Платформа Платформа 1С v8.x (все механизмы)
Конфигурация Не имеет значения
Операционная система Не имеет значения
Страна Не имеет значения
Отрасль Не имеет значения
Налоги Не имеет значения
Вид учета Не имеет значения
Раздел учета Не имеет значения
Доступ к файлу Бесплатно (free)
Код открыт Да
