Слияние двух почти одинаковых текстов - PullRequest
2 голосов
/ 09 ноября 2011

У меня возникла следующая хитрая проблема:

У меня есть два в основном идентичных текста, один из которых помечен тегом xml, а другой нет.

Правописание в тексте, содержащем xml-теги были нормализованы - что я не хочу.Вот почему я ищу способ слияния двух текстов: я должен заменить xml-текст очень похожим, но не идентичным простым текстом, сохраняя xml-структуру.

Кто-нибудь знает, возможно ли это?Есть ли способ решить проблему в Perl?

Большое спасибо!

Алекс


Пример

Нормализованный XML:

<div2>
<head>Title</head>
<p>Here is some normalized sample text.</p>
<p>The orthograph has been changed.</p>
</div2>

Из оригинального простого текста:

Титул

Вот несколько нормализованных образцов текста.

Ортограф имеетбыл изменен.

Я хотел бы получить вывод, подобный этому:

<div2>
<head>Title</head>
<p>Here is some normalised sample texte.</p>
<p>The ortographe has been changed.</p>
</div2>

Ответы [ 2 ]

1 голос
/ 09 ноября 2011

Хмм ... Я бы предложил использовать для этого Algorithm :: Diff . По сути, если вы взяли двухстороннюю разность двух текстов, вы должны получить что-то вроде этого:

[+<div2>+]
[+<head>+]Tit[-e-]l[+e</head>+]
[+<p>+]Here is some normali[-s-][+z+]ed sample text[-e-].[+</p>+]
[+<p>+]The ort[+h+]ograph[-e-] has been changed.[+</p>+]
[+</div2>+]

Вы заметите, что некоторые вставки тегов XML чередуются с текстовыми изменениями. Теперь, если вы просто взяли теги из версии + и текст из версии -, вы должны получить требуемый комбинированный текст.

Для достижения наилучшего эффекта я бы рекомендовал использовать интеллектуальный токенизатор, который обрабатывает теги XML как отдельные токены, например, <p>foo</p> будет разделен на <p>, f, o, o, </p>. Это не только ускоряет процесс сравнения и упрощает синтаксический анализ выходных данных, но также позволяет избежать риска того, что алгоритм сравнения может разбить тег на несколько частей или спутать его с текстом.

Вот пример кода:

sub merge_tags {
    my ($orig, $tagged) = @_;

    # tokenize strings into tags and chars (could use a real XML parser here)
    $_ = [/\G(<(?:[^>"']|"[^"]*"|'[^']*')*>|.)/sg] for $orig, $tagged;

    require Algorithm::Diff;
    my $diff = Algorithm::Diff->new( $orig, $tagged );

    my @output;
    while ($diff->Next) {
        if ($diff->Diff) {
            my @text = grep !/^<.*>$/s, $diff->Items(1);
            my @tags = grep  /^<.*>$/s, $diff->Items(2);
            # kluge: output opening tags first
            push @output, shift @tags while @tags and $tags[0] !~ /^<\//;
            push @output, @text, @tags;
        }
        else {
            push @output, $diff->Same;
        }
    }
    return join "", @output;
}

Я уверен, что этот код может быть улучшен (например, он может быть разумнее в отношении вложенности тегов), но, по крайней мере, он работает для примера ввода.

0 голосов
/ 09 ноября 2011

Если всегда есть одинаковое количество слов и одинаковый порядок - вы можете просто заменить слова одно на другое.

...