Понадобилось мне 2 строки сравнить и определить, какие в них фрагменты совпадают, а какие различаются. Для удобства привёл строки к одной длине. Впрочем, при желании любые 2 строки можно порубать так, чтобы сравнивать как имеющие одинаковую длину. Ну и ничего, кроме хитрых регулярных выражений да посимвольного перебора не нашёл. RegExp не удалось припахать к выдаче результата в нужном мне виде (возможно, мои руки кривы), а посимвольно обходить длинные строки не хотелось. В итоге сделал эту, авось кому пригодится.
Функция ПолучитьРазличияДвухСтрок(Знач стро1,Знач стро2,тф=Неопределено,Знач рДельта=0)
Если тф=Неопределено Тогда
// это первая итерация, инициализируемся
рМаксДлина=0;
// результаты вернём в таблице значений, фиксирующей фрагменты: № начсим, № консим, ЕстьРазница (булево)
тф=Новый ТаблицаЗначений;
тф.Колонки.Добавить(«Начало»);
тф.Колонки.Добавить(«Конец»);
тф.Колонки.Добавить(«ЕстьРазница»);
Если стро1=стро2 Тогда // разницы вообще нет
стротф=тф.Добавить();
стротф.Начало=1;
стротф.Конец=СтрДлина(стро2);
Возврат тф;
Иначе
рДлина1=СтрДлина(стро1);
рДлина2=СтрДлина(стро2);
Если рДлина1<>рДлина2 Тогда // можно обрезать под одну длину, можно и отказаться
рМинДлина=Мин(рДлина1,рДлина2);
рМаксДлина=Макс(рДлина1,рДлина2);
Если рМинДлина=рДлина1 Тогда стро2=Лев(стро2,рМинДлина) КонецЕсли;
Если рМинДлина=рДлина2 Тогда стро1=Лев(стро1,рМинДлина) КонецЕсли;
КонецЕсли;
КонецЕсли;
// заполним дихотомически
ПолучитьРазличияДвухСтрок(стро1,стро2,тф);
// дообработаем разницу длин, если она была, дописав «хвост» более длинной строки
Если рМаксДлина<>0 Тогда
стротф=тф.Добавить();
стротф.Начало=рМинДлина+1;
стротф.Конец=рМаксДлина;
стротф.ЕстьРазница=Истина; // априорно
КонецЕсли;
// грубо свернём (такая таблица никогда не будет очень большой, поэтому можно не изощряться)
тф2=тф.СкопироватьКолонки(); старЕР=Неопределено; старНачало=0; старКонец=0;
Для каждого стротф Из тф Цикл
Если старЕР<>стротф.ЕстьРазница Тогда
Если старЕР<>Неопределено Тогда // закончим предыдущую
стротф2=тф2.Добавить();
стротф2.Начало=старНачало;
стротф2.Конец=старКонец;
стротф2.ЕстьРазница=старЕР;
КонецЕсли;
старЕР=стротф.ЕстьРазница;
старНачало=стротф.Начало;
КонецЕсли;
старКонец=стротф.Конец;
КонецЦикла;
Если старЕР<>Неопределено Тогда // закончим предыдущую
стротф2=тф2.Добавить();
стротф2.Начало=старНачало;
стротф2.Конец=старКонец;
стротф2.ЕстьРазница=старЕР;
КонецЕсли;
Возврат тф2;
Иначе
// собственно рекурсивное сравнение строк
этстро=стро1; // строка-эталон
обрстро=стро2; // обрабатываемая строка
пози=Цел(СтрДлина(обрстро)/2);
Если пози=0 Тогда Возврат «» КонецЕсли; // ненормальная ситуация
кусэт1=Лев(этстро,пози);
кусэт2=Сред(этстро,пози+1);
кусобр1=Лев(обрстро,пози);
кусобр2=Сред(обрстро,пози+1);
изм1=(кусэт1<>кусобр1);
изм2=(кусэт2<>кусобр2);
// смотрим первый кусок
Если не изм1 Тогда
стротф=тф.Добавить();
стротф.Начало=?(рДельта=0,1,рДельта);
стротф.Конец=стротф.Начало+СтрДлина(кусобр1)-1;
стротф.ЕстьРазница=Ложь;
Иначе // эта часть различна, идём дальше, обрабатывая её как отдельную строку
рНачало=?(рДельта=0,1,рДельта);
рКонец=рНачало+СтрДлина(кусобр1)-1;
Если рНачало=рКонец Тогда // финальная фаза, 1 символ разницы
стротф=тф.Добавить();
стротф.Начало=рНачало;
стротф.Конец=рКонец;
стротф.ЕстьРазница=Истина;
Иначе
ПолучитьРазличияДвухСтрок(кусэт1,кусобр1,тф,рДельта);
КонецЕсли;
КонецЕсли;
// смотрим второй кусок
рДельта=(пози+1)+рДельта-?(рДельта=0,0,1);
Если не изм2 Тогда
стротф=тф.Добавить();
стротф.Начало=рДельта;
стротф.Конец=стротф.Начало+СтрДлина(кусобр2)-1;
стротф.ЕстьРазница=Ложь;
Иначе // эта часть различна, идём дальше, обрабатывая её как отдельную строку
рНачало=рДельта;
рКонец=рНачало+СтрДлина(кусобр2)-1;
Если рНачало=рКонец Тогда // финальная фаза, 1 символ разницы
стротф=тф.Добавить();
стротф.Начало=рНачало;
стротф.Конец=рКонец;
стротф.ЕстьРазница=Истина;
Иначе
ПолучитьРазличияДвухСтрок(кусэт2,кусобр2,тф,рДельта);
КонецЕсли;
КонецЕсли;
// ничего не возвращаем, результат не важен
КонецЕсли;
КонецФункции
Пример вызова:
тф=ПолучитьРазличияДвухСтрок(строка1,строка2);
Спасибо. Только недавно была нужна подобная фишка. Обз… прикручу!
Чересчур громоздкое решение. Вот мой вариант без дихотомии (15 строк):
Показать
Вот мой вариант с рекурсией и дихотомией (19 строк)
Показать
Может быть, данный пример заставит Вас изменить свое мнение по вопросукомментария ?
(2) Ну, вариант с посимвольным — неинтересно, а вот дихотомия да, изящна. Вы, часом, на Perl’е не работали? 🙂
По времени наши варианты практически одинаковы, замерено; в среднем, Ваш чуть-чуть быстрее.
Мнение насчёт комментария — нет, не изменит. Код должен быть читабелен и управляем. Компактность — лютое эстетство, отнюдь не всегда нужное.
Так что меряться количеством строк кода — дело бесползеное. Можно вообще всё в одну строку вытянуть, верно? Только вот пониманию и отладке это не способствует.
Варианты (2) не только компактней, но и понятней. А также доставляют эстетическое удовольствие, что немаловажно в нашем унылом мире 🙂
(4) На примере Вашей же задачи (кстати, спасибо за нее) применение функции НоваяТаблицаЗначений дает экономию три строки. Число строк в решении тогда становится волшебным (для программистов) числом 16. Вторая функция при этом будет выглядеть так
Неужели Вы считаете более понятным, наглядным, упрощающим отладку вариант
Показать
В нем полно воды! Три совершенно не информативных, избыточных строчки, 72 символа, которые отнимают Ваше внимание, на которые Вы никогда не поставите точки останова в отладчике. Как я уже говорил в комментариях в той статье, 72 символа — очень много. Это несколько анекдотов типа «повторение — мать заикания».
В первом варианте имена создаваемых колонок рядом, как на самом деле в таблице, не нужно тратить воображение на транспонирование ее шапки.
Извините за занудство, это была еще одна попытка перетащить Вас в свой лагерь любителей краткости.
(6) Короче, ага. Только вот тогда ещё и эта функция нужна всюду.
Вообще, я хорошо понимаю, что такое эстетика кода, лаконичность и прочее, иногда трачу изрядное время лишь на сокращение и «вылизывание» решения, просто у нас с Вами разные взгляды на конкретно такие вещи. Вот, например, с запросами замыканием я целиком согласен, сам нечто подобное делал и Вашими идеями пользуюсь, но насчёт создания таблицы значений мы расходимся. Ну вот не нравится мне такое. Массив так объявить ещё куда ни шло, а объявления типов, заголовки, ширины? Мне бывает надо; тогда вызов Вашей функции или недостаточен, или распухает в не менее уродливые переносы на несколько строк.
А что моё решение не самое красивое, а ваше — более стильное, не спорю; хорошо, что спровоцировал такие находки.
В ветку «разницы вообще нет» добавьте:
стротф.ЕстьРазница=Ложь;
Иначе значение реквизит «ЕстьРазница» неопределено.
А что такое дихотомический обход?
Кстати, вызванные данной статьей размышления над этой задачей и ее решением привели к мысли использовать тот же метод уже не для сравнения строк, а для сравнения движений документов в двух базах. В результате получился вот такой очень быстрый и полезный на практике отчетКомпаратор оборотов в информационных базах. За это еще одно спасибо.
что за дихотомия? о.О
(12) PiccaHut001, результат сравнения здесь показывает не просто «равно-не равно», а где равно, а где не равно, то есть показывает, например, все интервалы позиций равенства символов в двух строках. Например, кто-то что-то изменил в длинной строке, как найти место изменений. Посимвольное сравнение — долгое, а если сравнивать большие куски строк, то будет быстрее.
(13) ildarovich, Всё равно не понятно, как в 1С эту функцию можно использовать.
(14) PiccaHut001, ну это лучше у автора спрашивать. Ну а меня это натолкнуло на идею быстро сравнивать таким образом обороты по счетам или регистрам в двух разных базах(отчет«Компаратор оборотов в информационных базах» ). Впрочем, я об этом уже говорил в комментарии (10).
(16) Ivon, ага, такое завышенное самомнение у человека, который в каждой третьей публикации вообще сомневается, стоило ли выкладывать… Который про одну свою недавнюю публично написал «пардон, баян». Почитайте мои высказывания, ага:http://infostart.ru/public/447333/ Всем бы такое завышенное)))