gifts2017

Нечеткое сравнение текстов

Опубликовал Vladimir Vasiliev (vasvl123) в раздел Обработки - Универсальные обработки

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

Для нечеткого сравнения строк использован метод, описанный здесь.

На форме задаются значения двух параметров:

Глубина сравнения слов - сколько соседних слов слева и справа добавлять в сравнение к каждому слову текста (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>");
	
	ЭтаФорма.Результат = Текст.ПолучитьТекст();
	
КонецПроцедуры // СравнитьТексты()

Внимание! Процедура активно использует оперативную память, и в случае ее нехватки будет происходить вываливание из программы.

Процедура работает не быстро, поэтому для реального использования может потребоваться доработка.

Скачать файлы

Наименование Файл Версия Размер Кол. Скачив.
Нечеткое сравнение текстов под УФ
.epf 12,45Kb
02.04.16
4
.epf 1.0 12,45Kb 4 Скачать

См. также

PowerTools от 1 000
Подписаться Добавить вознаграждение
В этой теме еще нет сообщений.
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа