[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Сравнение двух текстов
FatCat
Туплю, не могу сообразить с какой стороны подступиться.
Нужно сравнить 2 текста и выделить различия...

Попробую лучше показать на примере.
  • На входе:
    1. Сложные медицинские задачи имеют простые и понятные инженерам неправильные решения.
      М.Козак
    2. Сложные медицинские вопросы имеют простые и понятные для инженеров неправильные решения.
      М.Коzак
  • На выходе:
    1. Сложные медицинские задачи имеют простые и понятные инженерам неправильные решения.
      М.Козак
    2. Сложные медицинские вопросы имеют простые и понятные для инженеров неправильные решения.
      © М.Коzак




Спустя 2 минуты, 50 секунд (1.09.2009 - 17:28) FatCat написал(а):
Нет, сравнение до первого различия легко: substr($pos,1) выкусывает символ и можно сравнивать посимвольно.
Не понимаю, как определить момент, когда различие кончилось и пошел дальше одинаковый кусок.

Спустя 2 минуты, 42 секунды (1.09.2009 - 17:30) solib написал(а):
ну первое что пришло в голову разбить текст на масив
$text1=explode (" ",$text);
и потом в цикле проверить различия
for($i=0; $i<=count($text1); $i++){}

хотя не думаю что это првильно. ) предложил как вариант

Спустя 7 минут, 57 секунд (1.09.2009 - 17:38) solib написал(а):
хм по той схеме которую я предложил наверно не получится(((
в задачи-вопросы различие покажет
а в инженерам - для инженеров будет сбито число масивов и дальше текст будет весь как будто изменён

Спустя 20 минут, 59 секунд (1.09.2009 - 17:59) xPoint написал(а):
можно еще вот так попробовать:
PHP
$str1='Сложные медицинские задачи имеют простые и понятные инженерам неправильные решения.
М.Козак'
;

$str2 'Сложные медицинские вопросы имеют простые и понятные для инженеров неправильные решения.
©М.Коzак'
;

//разбиваем на слова(можно на буквы циклом)
$array1 explode(' '$str1);
$array2 explode(' '$str2);
var_dump(array_diff($array1$array2));

выводит
Цитата
array
  2 => string 'задачи' (length=12)
  7 => string 'инженерам' (length=18)
  9 => string 'решения.
М.Козак' (length=29)

почти то что нужно

Спустя 54 минуты, 7 секунд (1.09.2009 - 18:53) FatCat написал(а):
Посмотреть бы, как в ворде реализована функция сравнения версий... Он это классно делает.

Спустя 1 час, 41 минута, 33 секунды (1.09.2009 - 20:35) live Uucyc написал(а):
может делать цикл в цикле...первым циклом определяем символ, с которого начинается различие и запускаем второй цикл, которым определяем где закончились различия. После приравниваем счетчик первого цикла к счетчику второго и начинаем опять искать следующее различие.
У меня такая идея smile.gif

Спустя 51 минута, 34 секунды (1.09.2009 - 21:27) sergeiss написал(а):
FatCat... Я попробую вспомнить, как я делал аналогичное сравнение. Только у меня были не слова, а массивы чисел. Но по своей сути задача идентичная.
Было это где-то года 2 тому тому назад.



Спустя 16 минут, 56 секунд (1.09.2009 - 21:43) sergeiss написал(а):
Вот, нашел код.

Идея тут такая. Есть 2 набора данных, параметры секторов сотовой связи. Первый из указанных подразумевается более ранним по дате.

Поэтому если в нем данные есть, и их нету во втором, то считается, что данные были удалены. Если данные есть во втором списке, и их нету в первом, то они были добавлены.
Если же найдены совпадающие (по ключу) записи, то тогда сравниваем их поэлементно.
Все данные записываются в массивы строк типа TStringList.
В итоге данные выводятся пользователю.

Я так думаю, что разобраться в алгоритме работы этой функции сможет любой человек, не знающий С++, но знающий ПХП smile.gif

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

Код
void __fastcall TFormCompare::DoCompareCell(void)
{
AnsiString Str, Val1, Val2, Filter="";
int Cell1, Cell2, LAC1, LAC2;
bool FirstDifference;
TStringList
  *CellsDeleted=new TStringList,
  *CellsNew=new TStringList,
  *ChangedParameters=new TStringList,
  *F=new TStringList;

// создать фильтр
if( UseFilter->Checked )
 Filter=WhereFilter();

// этап 1. Построение списка сот по обеим базам
Query1->SQL->Text="SELECT * from \'"+Set1->Text+"\\Cell.dbf\'";
if( Filter.Trim().Length() > 0 )
 Query1->SQL->Text = Query1->SQL->Text + " where "+Filter;
Query1->SQL->Text = Query1->SQL->Text +" order by LAC, CellID";

try
{
 Query1->Open();
}
catch(...)
{
 Memo->Lines->Add( "Ошибка при формировании списка сот из 1-го набора.");
 Memo->Lines->Add( "Запрос: \'"+Query1->SQL->Text+"\'");
 if( Query1->Active ) Query1->Close();
 delete CellsDeleted;
 delete CellsNew;
 delete ChangedParameters;
 delete F;
 return;
}

Query2->SQL->Text="SELECT * from \'"+Set2->Text+"\\Cell.dbf\'";
if( Filter.Trim().Length() > 0 )
 Query2->SQL->Text = Query2->SQL->Text + " where "+Filter;
Query2->SQL->Text = Query2->SQL->Text +" order by LAC, CellID";

try
{
 Query2->Open();
}
catch(...)
{
 Memo->Lines->Add( "Ошибка при формировании списка сот из 2-го набора.");
 Memo->Lines->Add( "Запрос: \'"+Query2->SQL->Text+"\'");
 if( Query1->Active ) Query1->Close();
 if( Query2->Active ) Query2->Close();
 delete CellsDeleted;
 delete CellsNew;
 delete ChangedParameters;
 delete F;
 return;
}

// этап 2 - собственно проверка данных
ProgressBar->Min=0;
ProgressBar->Max=Query1->RecordCount;
ProgressBar->Position=0;

// подготовить список полей в таблице
for( int i=0; i<Query1->FieldCount; i++)
 F->Add( Query1->FieldDefs->Items[i]->Name );

for(
 Query1->First(), Query2->First();
 !Query1->Eof && !Query2->Eof; /* переход на следующие записи - внутри цикла */ )
{
 ProgressBar->Position=Query1->RecNo;
 Application->ProcessMessages();

 Cell1=Query1->FieldByName("CellID")->AsInteger;
 Cell2=Query2->FieldByName("CellID")->AsInteger;
 LAC1=Query1->FieldByName("LAC")->AsInteger;
 LAC2=Query2->FieldByName("LAC")->AsInteger;


 if( Cell1 != Cell2 ) // не совпадают записи
 // без учета возможности разных LAC
 {
    if( Cell2 < Cell1 ) // во 2-м списке новая запись
    {
  Str=Str.sprintf("Новая сота LAC-CellID = %d-%d (CellRef=%d)",
   LAC2, Cell2, Query2->FieldByName("CellRef")->AsInteger);
  CellsNew->Add( Str );
  Query2->Next();
    }
    else
    {
  Str=Str.sprintf("Удалена сота LAC-CellID = %d-%d (CellRef=%d)",
   LAC1, Cell1, Query1->FieldByName("CellRef")->AsInteger);
  CellsDeleted->Add( Str );
  Query1->Next();
    }

    continue;
 }
 else
 // если записи совпадают,
 // то надо сравнить их поэлементно
 // функция DoCompareParameters как раз это и делает
 {
  DoCompareParameters( ChangedParameters, F, 1,
    LAC1, Cell1, LAC1, Cell1 );

  Query1->Next();
  Query2->Next();
 }

} // конец цикла по первому набору данных

Memo->Lines->Add( "Различия между конфигурациями");
Memo->Lines->Add( "\'" + Set1->Text.Trim() + "\' и " );
Memo->Lines->Add( "\'" + Set2->Text.Trim() + "\'" );
if( Filter.Trim().Length() > 0 )
 Memo->Lines->Add( "При использовании фильтра \'"+Filter+"\'" );

Memo->Lines->Add( "" );
if( CellsNew->Count > 0 || CellsDeleted->Count > 0 || ChangedParameters->Count > 0 )
 Memo->Lines->Add( "Данные по секторам." );
else
 Memo->Lines->Add( "По секторам изменений нет." );

if( CellsNew->Count > 0 )
{
 Memo->Lines->Add( "" );
 Memo->Lines->AddStrings( CellsNew );
}

if( CellsDeleted->Count > 0 )
{
 Memo->Lines->Add( "" );
 Memo->Lines->AddStrings( CellsDeleted );
}

if( ChangedParameters->Count > 0 )
{
 Memo->Lines->Add( "" );
 Memo->Lines->Add( "Измененные параметры секторов:" );
 Memo->Lines->AddStrings( ChangedParameters );
}

Query1->Close(); // Active=false;
Query2->Close(); // Active=false;
delete CellsDeleted;
delete CellsNew;
delete ChangedParameters;
delete F;
}

Спустя 1 час, 5 минут, 28 секунд (1.09.2009 - 22:49) Krevedko написал(а):
blink.gif
нет слов просто. оказалось, что я не знаю пхп )

Спустя 9 минут, 17 секунд (1.09.2009 - 22:58) xPoint написал(а):
эт не пхп, это С++ (кажется борландовски, поправьте если ошибаюсь) wink.gif

Спустя 21 минута, 47 секунд (1.09.2009 - 23:20) sergeiss написал(а):
xPoint - да, ты прав.
Но Krevedko подразумевал, как я понял, мою фразу "Я так думаю, что разобраться в алгоритме работы этой функции сможет любой человек, не знающий С++, но знающий ПХП". Вот он и говорит, мол, "нихт ферштейен". wink.gif

Но если он же посмотрит внимательно, то увидит структуру языка, подобную ПХП. Циклы, сравнения, формат записи...



Спустя 17 минут, 23 секунды (1.09.2009 - 23:37) SunSet написал(а):
Krevedko
Лишний раз убеждаюсь, что в такие топы лучше не заглядывать, дабы себя не огорчать laugh.gif

FatCat
Кстати, а почему в новичках такая тема?)) Что уж постить в "Для профи".. blink.gif

Спустя 1 час, 16 минут, 6 секунд (2.09.2009 - 00:54) FatCat написал(а):
Цитата (sergeiss @ 1.09.2009 - 22:43)
Все данные записываются в массивы строк

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

Спустя 7 часов, 44 секунды (2.09.2009 - 07:54) sergeiss написал(а):
FatCat - так а в чем проблема-то? Используем explode, получаем 2 массива. А потом идем по ним "параллельно", как у меня в приведенной мной функции. И сравниваем поэлементно. Нашли различие - поставили какую-то метку (в отдельном массиве для каждого текста).
Собственно говоря, сравнение в моей функции занимает строчек 10-15 где-то.

Спустя 2 часа, 33 секунды (2.09.2009 - 09:55) FatCat написал(а):
Цитата (sergeiss @ 2.09.2009 - 08:54)
Используем explode, получаем 2 массива.

По какому символу эксплодить? По пробелу?
А если изменена одна буква?
Сравнивать 2 массива букв? Что-то я сомневаюсь, что мы получим результат сравнения...


Я вроде сообразил как сделать. И нифига не массивами вообще. Потому что искать следует не различия, а сходства. Текст изначально считается разным, и затем ищутся совпадающие блоки текста и с них снимается раскраска. Попробую реализовать в виде кодов, посмотрим, что покажет в полевых испытаниях.

Спустя 1 час, 4 минуты, 51 секунда (2.09.2009 - 11:00) glock18 написал(а):
Цитата
Я вроде сообразил как сделать. И нифига не массивами вообще. Потому что искать следует не различия, а сходства. Текст изначально считается разным, и затем ищутся совпадающие блоки текста и с них снимается раскраска. Попробую реализовать в виде кодов, посмотрим, что покажет в полевых испытаниях.


Я к тому же дошел. Вообще то изначально попробовал это реализовать на добровольных началах, но дело пошло не очень то удачно. Единственное, что я понял точно нужно искать совпадающие куски (при этом кусок должен быть больше 1 символа обязательно) - я ставил три символа. По идее при таком подходе, приведенные две строки будут распарсены именно так, как раскрашены они в этом топике. Дальше идеи я правда недалеко ушел.

Спустя 56 минут, 10 секунд (2.09.2009 - 11:56) sergeiss написал(а):
FatCat - ты вредитель и провокатор smile.gif А также меньшевик, гегельянец и оппортунист...
Я "убил" где-то часа 2-3 рабочего времени, деля его между этой задачей и основными задачами по своей работе!

Но!!! В итоге все-таки сделал так, что находятся полные слова, различающиеся между 2-мя строками.

В примере $str1 - строка из хэлпа (преобразуется в массив $arr1), $str2 ($arr2) - эта же строка, только модифицированная. На выходе - 2 массива, $added и $deleted, содержащие соответственно добавленные и удаленные слова. Ключи в массиве $added соответствуют ключам из $arr2, а ключи в $deleted - ключам в $arr1.
Всё остальное прокомментировано внутри.

Ну, а уж искать расхождения по букоФФкам... Я думаю можно, но это уж без меня как-нибудь smile.gif

Вобщем-то, можно, я думаю, уже по окончании поиска расхождений слов поковыраться с найденными словами, с учетом их позиций... Но, как я сказал, это уж без меня как-нибудь.

PHP
// функция возвращает количество элементов массива $a, после которых находим слово (элемент массива), совпадающее со строкой $s
// если не найдено, то возвращает -1
function find_first_equal_element$s$a )
{
    
$c=0;
    foreach( 
$a as $v )
    {
        if( 
$s == $v ) return $c;
        
$c++;
    }
    return -
1;
}

// функция удаляет первый элемент указанного массива, перенося его в другой массив; и при этом сохраняется ключ этого массива
function array_clear_first( &$arr, &$arr_to_add )
{
    
$key=key$arr );
    
$arr_to_add$key ]=$arr$key];
    unset( 
$arr$key ] );
    return 
$arr_to_add$key ];
}

$str1='Обратите внимание, что эта функция обрабатывает только одно измерение многомерного массива. Разумеется, вы можете обработать более одного измерения, используя array_udiff($array1[0], $array2[0])';

$str2='Обратите внимание, эта функция обрабатывает более одного измерения многомерного массива. Разумеется, вы ни хрена не можете обработать более одного измерения, используя array_udiff($array1[0], $array2[0])';

echo 
"String1 : '$str1'<br>String2 : '$str2'<br>";

// готовим данные к обработке и вспомогательные массивы
$arr1=explode' '$str1);
$arr2=explode' '$str2);
$s1='';
$s2='';
$added=array();
$delete=array();

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

while( true /* выход из цикла - по условиям внутри него*/
{
 
$s1=reset$arr1 );
 
$s2=reset$arr2 );
 
 if( 
$s1 == false or $s2 == false ) break; // дошли до конца хотя бы в одном массиве
 
 
if( $s1 == $s2 )
 
// если совпадают - то тогда удаляем первые элементы и идем на следующий шаг
 
{
     unset( 
$arr1key($arr1) ] );
    unset( 
$arr2key($arr2) ] );
    continue;
 }
 
 else 
// не совпадают записи
 
{
     
// находим позицию совпадения второй стороки с элементами первого массива - что было добавлено
    
$diff_add=find_first_equal_element$s2$arr1 );
     
// находим позицию совпадения первой стороки с элементами второго массива - что было удалено
    
$diff_del=find_first_equal_element$s1$arr2 );
    
    
// проверка на уникальные значения
    
if( $diff_del == -)
    {
        
$del=array_clear_first$arr1$deleted );
        continue;
    }
    if( 
$diff_add == -)
    {
        
$del=array_clear_first$arr2$added);
        continue;
    }
    
    
// если оба - неуникальные значения, тов цикле удаляем элементы массива в количестве, равном минимальной найденной позиции из 2-х

    
$diff=min$diff_add$diff_del ); // находим минимальную позицию

    
for( $i=0$i<$diff$i++)
    {
        if( 
$diff_del >= $diff_add )
            
$del=array_clear_first$arr1$deleted );
        if( 
$diff_del <= $diff_add )
            
$del=array_clear_first$arr2$added ); 
    }
    
 } 
// конец обработчика несовпадений    
// конец общего цикла

// и теперь только надо добавить к итоговым массивам (возможно) оставшиеся "хвосты" - с сохранением ключей
foreach( $arr1 as $k => $v )
    
$deleted$k ]=$v;

foreach( 
$arr2 as $k => $v )
    
$added$k ]=$v;

echo 
'Added :';
print_r$added );
echo 
'<br>Deleted : ';
print_r$deleted );


На выходе получаем ожидаемый результат:
Код
Added :Array ( [2] => что [6] => только [7] => одно [8] => измерение )
Deleted : Array ( [5] => более [6] => одного [7] => измерения [12] => ни [13] => хрена [14] => не )

Спустя 6 часов, 41 минута, 21 секунда (2.09.2009 - 18:37) FatCat написал(а):
Цитата (sergeiss @ 2.09.2009 - 12:56)
В итоге все-таки сделал так, что находятся полные слова, различающиеся между 2-мя строками.

У меня вроде получается сделать сравнение с точностью до оного символа. Сейчас маюсь с html-тегами, чтобы теги разметки не попадали внутрь тега если у фразы например жирность на курсив поменяли.

И ни фига не массивами. laugh.gif

Идея довольно проста.
1. Ползем от начала текста, ищем первое различие. Отрезаем.
2. Ползем справа налево от конца текста до первого различия. Отрезаем.
3. В имеющемся тексте ищем сходства: ползем посимвольно и проверяем на уникальные совпадения; в начало таких кусков ставим закрывающий тег, в конец таких кусков открывающий. wink.gif

Да, конечно, не без хитросетй, иначе вложенный цикл по числу символов сожрет слишком много ресурсов. Хитрость в том, что по инкременту нарастает отступ и длина сверяемого куска; как получили уникальность совпадения - так стали расползаться в обе стороны, выбирая таким образом весь кусок уникального совпадения.

Спустя 2 часа, 22 минуты, 13 секунд (2.09.2009 - 20:59) FatCat написал(а):
Yes!!!! Я сделал это!

user posted image

Спустя 1 день, 50 минут, 33 секунды (3.09.2009 - 21:50) FatCat написал(а):

Спустя 4 минуты, 31 секунда (3.09.2009 - 21:54) SunSet написал(а):
FatCat
Вещь, вроде, полезная, только мало где применима..
Подскажи, а для чего к скриптам приделывают время его генерирования? Для наочности просто? huh.gif

Спустя 4 минуты, 34 секунды (3.09.2009 - 21:59) FatCat написал(а):
Цитата (SunSet @ 3.09.2009 - 22:54)
для чего к скриптам приделывают время его генерирования?

Скрипт кушает немало ресурсов. Таймер помогает оценить в цифрах это "немало".

Спустя 2 минуты, 48 секунд (3.09.2009 - 22:02) SunSet написал(а):
FatCat
А этот таймер прикручивается именно к конкретному скрипту или ко всей загружаемой странице? Штука полезная как вижу.. cool.gif

Спустя 2 минуты, 17 секунд (3.09.2009 - 22:04) FatCat написал(а):
К скрипту. Время загрузки страницы пользователем меня не интересует.

Спустя 1 месяц, 20 дней, 22 часа, 1 минута, 42 секунды (24.10.2009 - 20:06) alko написал(а):
Вот правильная реализация www.easywebscripts.net/php/php_text_differences.php


_____________
Бесплатному сыру в дырки не заглядывают...
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.