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

02.04.16

Задачи пользователя - Разное

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

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

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

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

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

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

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

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

нечеткий поиск сравнение текстов

См. также

Разложим фото по полочкам

Разное Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

В публикации представлена простая внешняя обработка, позволяющая перенести файлы с фото в нужные папки.

1 стартмани

21.03.2024    338    1    vikhrov53    0    

3

Учет домашней квартплаты

Разное Платформа 1С v8.3 Конфигурации 1cv8 Энергетика и ЖКХ Россия Бухгалтерский учет Управленческий учет Абонемент ($m)

Позволяет в одном документе собрать записи по всем коммунальным платежам и показаниям бытовых приборов. Вы не пропустите и не забудете свой коммунальный платеж. Будете точно знать ежемесячную сумму коммуналки по всем объектам собственности. Расширение предназначено для самой популярной конфигурации 1С:Бухгалтерия 8, ред. 3. Объекты расширения не имеют заимствования конфигурации. Его легко встроить в другие типовые конфигурации 1С.

1 стартмани

30.01.2024    470    2    kwn2000    2    

5

Конфигурация "Рюкзачок"

Разное Платформа 1С v8.3 Туризм и путешествия Россия Абонемент ($m)

Составление комплекта вещей в поездку, поход, для работы и т.п. Конфигурация разработана для платформы 1С:Предприятие 8.3 (конкретно учебная версия 8.3.13.1644, потому что более новые утомили меня своими сюрпризами).

1 стартмани

19.01.2024    6122    5    AnatolPopov    31    

36

Конфигурация "Казначей группы (класса/команды/...)"

Разное Взаиморасчеты Платформа 1С v8.3 Общественные и некоммерческие структуры Обучение, бизнес-тренинг, курсы Развлечения, искусство, спорт Домашние учет и финансы Управленческий учет Абонемент ($m)

Нано- (даже не микро- !) конфигурация для тех, кто выступает в роли казначея класса/группы/команды, и в чьи задачи входит сбор денег с участников за участие в каких-то мероприятиях, а потом передача собранных денег организаторам этих мероприятий.

1 стартмани

03.11.2023    671    3    q_i    0    

11

Математический тренажер через телеграмм (feat 1С)

Математика и алгоритмы Разное Платформа 1С v8.3 Конфигурации 1cv8 Россия Абонемент ($m)

Расширение (+ обработка) представляют собою математический тренажер. Ваш ребенок сможет проверить свои знание на математические вычисление до 100.

2 стартмани

29.09.2023    1898    maksa2005    8    

22

Генератор математических примеров

Математика и алгоритмы Разное Абонемент ($m)

Маленькая программа для генерации примеров. C#

1 стартмани

28.09.2023    1285    1    FIERYLIONS    18    

7

Конструктор: ЧислоПрописью и СтрокаСЧислом

Разное Инструментарий разработчика Работа с интерфейсом Платформа 1С v8.3 Абонемент ($m)

Возможно я просто ленивый, а может потому что понадобилось... в общем сделал конструктор параметров. Может 1С когда-нибудь сделает что-нибудь такое в конфигуратор?

1 стартмани

15.09.2023    3056    11    vandalsvq    8    

48

Viewer 1.0 (библиотека картинок, wingding, webdings, символы по кодам)

Разное Инструментарий разработчика Платформа 1С v8.3 Конфигурации 1cv8 Россия Абонемент ($m)

Иногда нужно посмотреть все картинки из библиотеки картинок или шрифты Webdings/Wingdings, вот собственно эта обработка для этого...

1 стартмани

04.09.2023    765    2    Незнайка    0    

11
Оставьте свое сообщение