Для нечеткого сравнения строк использован метод, описанный здесь.
На форме задаются значения двух параметров:
Глубина сравнения слов - сколько соседних слов слева и справа добавлять в сравнение к каждому слову текста (0 - не добавлять). При увеличении значения глубины сравнения слов улучшается качество сравнения, т.к. учитывается последовательность расположения слов. Однако это ведет к существенному замедлению работы процедуры. Также при увеличении глубины, потребуется понижение порога сходства строк.
Порог сходства строк - учитывать результаты сравнения выше определенного процента (0 - учитывать все, 99 - только точное соответствие)
В самой процедуре жестко прописан порог отсечения коротких цепочек слов (меньше 5% от среднего ранга).
Тексты для сравнения можно вставлять из буфера обмена или из файлов (можно выбирать несколько).
Ниже приведена процедура сравнения текстов. Текст процедуры содержит комментарии, поэтому разобраться будет не сложно.
&НаСервере
Процедура СравнитьТексты()
Объект.ТаблицаДанных.Очистить();
Объект.Соответствия.Очистить();
ТекстИсходный = Новый ТекстовыйДокумент;
ТекстЗамена = Новый ТекстовыйДокумент;
// Выбрать первые два текста для сравнения
Для НомерТекста = 1 По ЭтаФорма.КоличествоТекстов Цикл
Если ТекстИсходный.КоличествоСтрок() = 0 Тогда
ТекстИсходный = ЭтаФорма["ЗначениеТекст" + НомерТекста];
Продолжить;
КонецЕсли;
Если ТекстЗамена.КоличествоСтрок() = 0 Тогда
ТекстЗамена = ЭтаФорма["ЗначениеТекст" + НомерТекста];
КонецЕсли;
КонецЦикла;
Если ТекстИсходный.КоличествоСтрок() = 0 ИЛИ ТекстЗамена.КоличествоСтрок() = 0 Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Для сравнения необходимо два текста";
Сообщение.Сообщить();
Возврат;
КонецЕсли;
// Разбить тексты на слова
ТекстИсходный = СтрЗаменить(ТекстИсходный.ПолучитьТекст(), Символы.ПС, "<br>");
ТекстИсходный = СтрЗаменить(ТекстИсходный, " ", " " + Символы.ПС);
ТекстИсходный = СтрЗаменить(ТекстИсходный, ".", "." + Символы.ПС);
ТекстИсходный = СтрЗаменить(ТекстИсходный, ",", "," + Символы.ПС);
ТекстИсходный = СтрЗаменить(ТекстИсходный, "!", "!" + Символы.ПС);
ТекстИсходный = СтрЗаменить(ТекстИсходный, "?", "?" + Символы.ПС);
ТекстЗамена = СтрЗаменить(ТекстЗамена.ПолучитьТекст(), Символы.ПС, "<br>");
ТекстЗамена = СтрЗаменить(ТекстЗамена, " ", " " + Символы.ПС);
ТекстЗамена = СтрЗаменить(ТекстЗамена, ".", "." + Символы.ПС);
ТекстЗамена = СтрЗаменить(ТекстЗамена, ",", "," + Символы.ПС);
ТекстЗамена = СтрЗаменить(ТекстЗамена, "!", "!" + Символы.ПС);
ТекстЗамена = СтрЗаменить(ТекстЗамена, "?", "?" + Символы.ПС);
СтрЧислоСтрокТекстИсходный = СтрЧислоСтрок(ТекстИсходный);
СтрЧислоСтрокТекстЗамена = СтрЧислоСтрок(ТекстЗамена);
// Обработать первый текст
с = 0;
Для н = 1 По СтрЧислоСтрокТекстИсходный Цикл
с = с + 1;
Строка = СтрПолучитьСтроку(ТекстИсходный, н);
// Подготовить таблицу соответствий
НоваяСтрока = Объект.Соответствия.Добавить();
НоваяСтрока.Файл = "Источник";
НоваяСтрока.НомерИсточник = с;
НоваяСтрока.Строка1 = Строка;
Для н1 = 1 По ЭтаФорма.ГлубинаСравненияСлов Цикл
стр1 = "";
стр2 = "";
Если н - н1 > 0 Тогда
Стр1 = СтрПолучитьСтроку(ТекстИсходный, н - н1);
КонецЕсли;
Если н + н1 < СтрЧислоСтрокТекстИсходный + 1 Тогда
Стр2 = СтрПолучитьСтроку(ТекстИсходный, н + н1);
КонецЕсли;
Строка = стр1 + Строка + Стр2;
КонецЦикла;
// Заполнить таблицу сравнений
НовыйЭлемент = Объект.ТаблицаДанных.Добавить();
НовыйЭлемент.СтрокаНомер = с;
НовыйЭлемент.Строка = Строка;
НовыйЭлемент.Длина = СтрДлина(Строка);
КонецЦикла;
// Загрузить таблицу сравнений в память
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Таблица.СтрокаНомер КАК СтрокаНомер,
| Таблица.Строка КАК Строка,
| Таблица.Длина КАК Длина
|ПОМЕСТИТЬ ТаблицаДанных
|ИЗ
| &Таблица КАК Таблица";
ВремМен = Новый МенеджерВременныхТаблиц;
Запрос.Параметры.Вставить("Таблица", Объект.ТаблицаДанных.Выгрузить());
Запрос.МенеджерВременныхТаблиц = ВремМен;
РезультатЗапроса = Запрос.Выполнить();
Запрос2 = "";
Запрос3 = "";
// Обработать второй текст
Для н = 1 По СтрЧислоСтрокТекстЗамена Цикл
с = с + 1;
Строка = СтрПолучитьСтроку(ТекстЗамена, н);
// Дополнить таблицу соответствий
НоваяСтрока = Объект.Соответствия.Добавить();
НоваяСтрока.Файл = "Замена";
НоваяСтрока.НомерИсточник = с;
НоваяСтрока.Строка1 = Строка;
Для н1 = 1 По ЭтаФорма.ГлубинаСравненияСлов Цикл
стр1 = "";
стр2 = "";
Если н - н1 > 0 Тогда
Стр1 = СтрПолучитьСтроку(ТекстЗамена, н - н1);
КонецЕсли;
Если н + н1 < СтрЧислоСтрокТекстЗамена + 1 Тогда
Стр2 = СтрПолучитьСтроку(ТекстЗамена, н + н1);
КонецЕсли;
Строка = стр1 + Строка + Стр2;
КонецЦикла;
СтрокаПоиска = Лев(Строка, 500);
ДлинаСтроки = СтрДлина(СтрокаПоиска);
// выполнить поиск
Если (ДлинаСтроки > 2) И (ПорогСходстваСтрок < 99) Тогда
// Сформировать запрос для нечеткого поиска
Запрос = Новый Запрос;
ТекстПоиск = "0";
Для Индекс = 1 По ДлинаСтроки - 2 Цикл
ТекстПоиск = ТекстПоиск + " + ВЫБОР КОГДА ТаблицаДанных.Строка ПОДОБНО &тр" + Формат(Индекс, "ЧН=0; ЧГ=0") + " ТОГДА 1 ИНАЧЕ 0 КОНЕЦ";
Запрос.Параметры.Вставить("тр" + Индекс, "%" + Сред(СтрокаПоиска, Индекс, 3) + "%");
КонецЦикла;
ТекстДлина = "ВЫБОР КОГДА ПОДСТРОКА(ТаблицаДанных.Строка, " + Формат(ДлинаСтроки + 1, "ЧН=0; ЧГ=0") + ", 3) = """" ТОГДА " + Формат(ДлинаСтроки, "ЧН=0; ЧГ=0") + " ИНАЧЕ ТаблицаДанных.Длина КОНЕЦ";
Запрос.Текст =
"ВЫБРАТЬ
| " + Формат(с, "ЧН=0; ЧГ=0") + " КАК НомерСтрокиИзмененной,
| ТаблицаДанных.СтрокаНомер КАК НомерСтрокиИсходный,
| &СтрокаПоиска КАК СтрокаИзмененная,
| &СтрокаПоискаДлина КАК СтрокаИзмененнаяДлина,
| ТаблицаДанных.Строка КАК СтрокаИсходная,
| 100*(" + ТекстПоиск + ")/(" + ТекстДлина + " - 2) КАК Ранг
|ПОМЕСТИТЬ ТаблицаРезультатов" + Формат(с, "ЧН=0; ЧГ=0") + "
|ИЗ ТаблицаДанных КАК ТаблицаДанных
|ГДЕ 100*(" + ТекстПоиск + ")/(" + ТекстДлина + " - 2) > &Порог
|";
Запрос.МенеджерВременныхТаблиц = ВремМен;
Запрос.Параметры.Вставить("Порог", ЭтаФорма.ПорогСходстваСтрок);
Запрос.Параметры.Вставить("СтрокаПоиска", СтрокаПоиска);
Запрос.Параметры.Вставить("СтрокаПоискаДлина", ДлинаСтроки);
РезультатЗапроса = Запрос.Выполнить();
Иначе
// Сформировать запрос для простого сравнения коротких строк
// или если выбран порог сходства строк = 99
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| " + Формат(с, "ЧН=0; ЧГ=0") + " КАК НомерСтрокиИзмененной,
| ТаблицаДанных.СтрокаНомер КАК НомерСтрокиИсходный,
| &СтрокаПоиска КАК СтрокаИзмененная,
| &СтрокаПоискаДлина КАК СтрокаИзмененнаяДлина,
| ТаблицаДанных.Строка КАК СтрокаИсходная,
| 100 КАК Ранг
|ПОМЕСТИТЬ ТаблицаРезультатов" + Формат(с, "ЧН=0; ЧГ=0") + "
|ИЗ ТаблицаДанных КАК ТаблицаДанных
|ГДЕ ТаблицаДанных.Строка = &СтрокаПоиска
|";
Запрос.МенеджерВременныхТаблиц = ВремМен;
Запрос.Параметры.Вставить("СтрокаПоиска", СтрокаПоиска);
Запрос.Параметры.Вставить("СтрокаПоискаДлина", ДлинаСтроки);
РезультатЗапроса = Запрос.Выполнить();
КонецЕсли;
// лишние таблицы потом удалить
Запрос3 = Запрос3 + "
|УНИЧТОЖИТЬ ТаблицаРезультатов" + Формат(с, "ЧН=0; ЧГ=0") + ";";
// объединить результаты в одну таблицу
Если Запрос2 = "" Тогда
Запрос2 = "
|ВЫБРАТЬ
| ТаблицаРезультатов.НомерСтрокиИзмененной КАК НомерСтрокиИзмененной,
| ТаблицаРезультатов.НомерСтрокиИсходный КАК НомерСтрокиИсходный,
| ТаблицаРезультатов.СтрокаИзмененная КАК СтрокаИзмененная,
| ТаблицаРезультатов.СтрокаИзмененнаяДлина КАК СтрокаИзмененнаяДлина,
| ТаблицаРезультатов.СтрокаИсходная КАК СтрокаИсходная,
| ТаблицаРезультатов.Ранг КАК Ранг
|ПОМЕСТИТЬ ТаблицаРезультатов
|ИЗ ТаблицаРезультатов" + Формат(с, "ЧН=0; ЧГ=0") + " КАК ТаблицаРезультатов";
Иначе
Запрос2 = Запрос2 + "
|ОБЪЕДИНИТЬ ВСЕ
|ВЫБРАТЬ
| ТаблицаРезультатов.НомерСтрокиИзмененной КАК НомерСтрокиИзмененной,
| ТаблицаРезультатов.НомерСтрокиИсходный КАК НомерСтрокиИсходный,
| ТаблицаРезультатов.СтрокаИзмененная КАК СтрокаИзмененная,
| ТаблицаРезультатов.СтрокаИзмененнаяДлина КАК СтрокаИзмененнаяДлина,
| ТаблицаРезультатов.СтрокаИсходная КАК СтрокаИсходная,
| ТаблицаРезультатов.Ранг КАК Ранг
|ИЗ ТаблицаРезультатов" + Формат(с, "ЧН=0; ЧГ=0") + " КАК ТаблицаРезультатов";
КонецЕсли;
КонецЦикла;
Запрос = Новый Запрос;
Запрос.Текст = Запрос2;
Запрос.МенеджерВременныхТаблиц = ВремМен;
РезультатЗапроса = Запрос.Выполнить();
Запрос = Новый Запрос;
Запрос.Текст = Запрос3;
Запрос.МенеджерВременныхТаблиц = ВремМен;
РезультатЗапроса = Запрос.Выполнить();
// Отсортировать результаты по рангу
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТаблицаРезультатов.НомерСтрокиИсходный КАК НомерСтрокиИсходный,
| ТаблицаРезультатов.НомерСтрокиИзмененной КАК НомерСтрокиИзмененной,
| ТаблицаРезультатов.СтрокаИзмененнаяДлина КАК СтрокаИзмененнаяДлина,
| 0 КАК СтрокаПредыдущая,
| ТаблицаРезультатов.Ранг КАК Ранг,
| ТаблицаРезультатов.Ранг * ТаблицаРезультатов.СтрокаИзмененнаяДлина КАК СуммаРанг
|ИЗ
| ТаблицаРезультатов КАК ТаблицаРезультатов
|
|УПОРЯДОЧИТЬ ПО
| ТаблицаРезультатов.НомерСтрокиИсходный,
| ТаблицаРезультатов.Ранг * ТаблицаРезультатов.СтрокаИзмененнаяДлина УБЫВ";
Запрос.МенеджерВременныхТаблиц = ВремМен;
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
НомерСтрокиИсходный = 0;
НомерСтрокиИзмененной = 0;
СуммаРанг = 0;
// Построить цепочки совпадающих последовательностей слов
н = 1;
СуммаСуммаРанг = 0;
Пока н < РезультатЗапроса.Количество() + 1 Цикл
Выборка = РезультатЗапроса[н - 1];
м = 1;
Пока н + м < РезультатЗапроса.Количество() + 1 Цикл
Выборка1 = РезультатЗапроса[н + м - 1];
Если Выборка1.СтрокаПредыдущая = 0 И Выборка1.НомерСтрокиИсходный - Выборка.НомерСтрокиИсходный = 1 И Выборка1.НомерСтрокиИзмененной - Выборка.НомерСтрокиИзмененной = 1 Тогда
Выборка1.СтрокаПредыдущая = н;
СуммаРанг = Выборка1.СуммаРанг;
Выборка1.СуммаРанг = Выборка.СуммаРанг + СуммаРанг;
СуммаСуммаРанг = СуммаСуммаРанг + СуммаРанг;
Пока НЕ Выборка1.СтрокаПредыдущая = 0 Цикл
Выборка1 = РезультатЗапроса[Выборка1.СтрокаПредыдущая - 1];
Выборка1.СуммаРанг = Выборка1.СуммаРанг + СуммаРанг;
СуммаСуммаРанг = СуммаСуммаРанг + СуммаРанг;
КонецЦикла;
Прервать;
КонецЕсли;
Если Выборка1.НомерСтрокиИсходный - Выборка.НомерСтрокиИсходный > 1 Тогда
Прервать;
КонецЕсли;
м = м + 1;
КонецЦикла;
н = н + 1;
КонецЦикла;
// задать порог для исключения коротких цепочек
ПорогСуммаРанг = 0;
Если РезультатЗапроса.Количество() Тогда
ПорогСуммаРанг = 0.05 * СуммаСуммаРанг / РезультатЗапроса.Количество();
КонецЕсли;
// отсортировать по длине цепочек
РезультатЗапроса.Сортировать("СуммаРанг УБЫВ");
// заполнить таблицу соответствий
Для каждого Выборка Из РезультатЗапроса Цикл
Если Выборка.СуммаРанг < ПорогСуммаРанг Тогда
Прервать;
КонецЕсли;
СтрокаИсходная = Объект.Соответствия.Получить(Выборка.НомерСтрокиИсходный - 1);
СтрокаЗамена = Объект.Соответствия.Получить(Выборка.НомерСтрокиИзмененной - 1);
Если СтрокаЗамена.Ранг = 0 И СтрокаИсходная.Ранг = 0 Тогда
СтрокаИсходная.НомерЗамена = Выборка.НомерСтрокиИзмененной;
СтрокаИсходная.Ранг = Выборка.Ранг;
СтрокаИсходная.Строка2 = СтрокаЗамена.Строка1;
СтрокаЗамена.Ранг = Выборка.Ранг;
СтрокаЗамена.НомерЗамена = Выборка.НомерСтрокиИсходный;
КонецЕсли;
КонецЦикла;
// дополнить длинные цепочки
СуммаСхожесть = 0;
Объект.Соответствия.Сортировать("НомерИсточник");
ПоследнийНомерИсточник = 0;
Для Каждого Строка Из Объект.Соответствия Цикл
СуммаСхожесть = СуммаСхожесть + Строка.Ранг;
Если Строка.Файл = "Замена" Тогда
Если Строка.НомерЗамена = 0 Тогда
Строка.НомерЗамена = Строка.НомерИсточник;
Строка.НомерИсточник = ПоследнийНомерИсточник;
Строка.Строка2 = Строка.Строка1;
Строка.Строка1 = "";
Иначе
ПоследнийНомерИсточник = Строка.НомерЗамена;
КонецЕсли;
КонецЕсли;
КонецЦикла;
// вывести результат
Объект.Соответствия.Сортировать("НомерИсточник, НомерЗамена");
Текст = новый ТекстовыйДокумент;
Текст.ДобавитьСтроку("<HTML><BODY><H4>Схожесть текстов " + Строка(Цел(СуммаСхожесть / Объект.Соответствия.Количество())) + "%</H4><br>");
Для Каждого Строка Из Объект.Соответствия Цикл
Если Строка.Файл = "Источник" ИЛИ Строка.Ранг = 0 Тогда
Текст.ДобавитьСтроку(ВывестиТаблицуРазличийВHTML(Строка.Строка1, Строка.Строка2));
КонецЕсли;
КонецЦикла;
Текст.ДобавитьСтроку("</BODY></HTML>");
ЭтаФорма.Результат = Текст.ПолучитьТекст();
КонецПроцедуры // СравнитьТексты()
Внимание! Процедура активно использует оперативную память, и в случае ее нехватки будет происходить вываливание из программы.
Процедура работает не быстро, поэтому для реального использования может потребоваться доработка.