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

02.04.16

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

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

Скачать файл

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Наименование По подписке [?] Купить один файл
Нечеткое сравнение текстов под УФ
.epf 12,45Kb ver:1.0
9
9 Скачать (1 SM) Купить за 1 850 руб.

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

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

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

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

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

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

См. также

Разное Аудит и бухгалтерские услуги, юриспруденция Россия Абонемент ($m)

Современные NDA становятся всё жёстче, а исполнители сталкиваются с необоснованными штрафами и рисками. В статье разбираем ключевые юридические нюансы, даём рекомендации по снижению рисков при подписании NDA и делимся практическими советами. Как грамотно ограничить штрафы, уточнить понятие конфиденциальной информации, сократить срок действия NDA и зафиксировать свою позицию в контракте? Читайте в нашем материале.

1 стартмани

17.02.2025    518    Adapta    1    

4

Разное Платформа 1С v8.3 1С:Комплексная автоматизация 2.х Абонемент ($m)

Универсальное расширение для работы с колесом баланса в любой конфигурации 1С с сохранением истории предыдущих оценок.

1 стартмани

04.02.2025    537    1    itsys    0    

3

Разное Платформа 1С v8.3 1C:Бухгалтерия Абонемент ($m)

Продукт для ведения родового дерева, поможет решить задачи по систематизации и визуализации информации по родословной.

10 стартмани

12.09.2024    2112    7    Kassper    9    

32

Разное Платформа 1С v8.3 1C:Бухгалтерия Россия Абонемент ($m)

Тренажер Азбуки Морзе на 1С без внешних компонент.

1 стартмани

17.04.2024    1476    2    Irwin    2    

13

Разное Платформа 1С v8.3 1C:Бухгалтерия Абонемент ($m)

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

1 стартмани

21.03.2024    964    1    vikhrov53    0    

3

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

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

1 стартмани

30.01.2024    1198    4    kwn2000    2    

6

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

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

1 стартмани

19.01.2024    7312    6    AnatolPopov    33    

39

Linux Разное Программист Пользователь Платформа 1С v8.3 Абонемент ($m)

Пример обработки для определения длительности аудиофайла в среде Linux.

1 стартмани

12.01.2024    1000    0    ErAK    3    

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