Я рад сообщить, что только что обнаружил " RegexpTrie ", который является полезной заменой кода и необходим для Perl's Regexp :: Assemble.
Установите его и попробуйте:
require 'regexp_trie'
foo = %w(miss misses missouri mississippi)
RegexpTrie.union(foo)
# => /miss(?:(?:es|ouri|issippi))?/
RegexpTrie.union(foo, option: Regexp::IGNORECASE)
# => /miss(?:(?:es|ouri|issippi))?/i
Вот сравнение результатов.Первые закомментированные выходные данные в массиве взяты из Regexp :: Assemble, а конечный вывод - из RegexpTrie:
require 'regexp_trie'
[
'how now brown cow', # /(?:[chn]ow|brown)/
'the rain in spain stays mainly on the plain', # /(?:(?:(?:(?:pl|r)a)?i|o)n|s(?:pain|tays)|mainly|the)/
'jackdaws love my giant sphinx of quartz', # /(?:jackdaws|quartz|sphinx|giant|love|my|of)/
'fu foo bar foobar', # /(?:f(?:oo(?:bar)?|u)|bar)/
'ms miss misses missouri mississippi' # /m(?:iss(?:(?:issipp|our)i|es)?|s)/
].each do |s|
puts "%-43s # /%s/" % [s, RegexpTrie.union(s.split).source]
end
# >> how now brown cow # /(?:how|now|brown|cow)/
# >> the rain in spain stays mainly on the plain # /(?:the|rain|in|s(?:pain|tays)|mainly|on|plain)/
# >> jackdaws love my giant sphinx of quartz # /(?:jackdaws|love|my|giant|sphinx|of|quartz)/
# >> fu foo bar foobar # /(?:f(?:oo(?:bar)?|u)|bar)/
# >> ms miss misses missouri mississippi # /m(?:iss(?:(?:es|ouri|issippi))?|s)/
Относительно того, как использовать ссылку на Википедию и слова с ошибками:
require 'nokogiri'
require 'open-uri'
require 'regexp_trie'
URL = 'https://en.wikipedia.org/wiki/Wikipedia:Lists_of_common_misspellings/For_machines'
doc = Nokogiri::HTML(open(URL))
corrections = doc.at('div#mw-content-text pre').text.lines[1..-1].map { |s|
a, b = s.chomp.split('->', 2)
[a, b.split(/,\s+/) ]
}.to_h
# {"abandonned"=>["abandoned"],
# "aberation"=>["aberration"],
# "abilityes"=>["abilities"],
# "abilties"=>["abilities"],
# "abilty"=>["ability"],
# "abondon"=>["abandon"],
# "abbout"=>["about"],
# "abotu"=>["about"],
# "abouta"=>["about a"],
# ...
# }
misspelled_words_regex = /\b(?:#{RegexpTrie.union(corrections.keys, option: Regexp::IGNORECASE).source})\b/i
# => /\b(?:(?:a(?:b(?:andonned|eration|il(?:ityes|t(?:ies|y))|o(?:ndon(?:(?:ed|ing|s))?|tu|ut(?:it|the|a)...
В этот момент вы можете использовать gsub(misspelled_words_regex, corrections)
, однако значения в corrections
содержат некоторые массивы, поскольку для замены слова с ошибкой можно было использовать несколько слов или фраз.Вам нужно будет что-то сделать, чтобы определить, какой из вариантов использовать.
В Ruby отсутствует очень полезный модуль, найденный в Perl, который называется Regexp :: Assemble .Python имеет hachoir-regex , который, кажется, делает то же самое.
Regexp :: Assemble создает очень эффективное регулярное выражение, основанное на списках слов и простых выражениях.Это действительно замечательно ... или ... дьявольски?
Посмотрите пример для модуля;Его очень просто использовать в его основной форме:
use Regexp::Assemble;
my $ra = Regexp::Assemble->new;
$ra->add( 'ab+c' );
$ra->add( 'ab+-' );
$ra->add( 'a\w\d+' );
$ra->add( 'a\d+' );
print $ra->re; # prints a(?:\w?\d+|b+[-c])
Обратите внимание, как он комбинирует шаблоны.Он сделал бы то же самое с обычными словами, только он был бы еще более эффективным, потому что будут объединены общие строки:
use Regexp::Assemble;
my $lorem = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.';
my $ra = Regexp::Assemble->new('flags' => 'i');
$lorem =~ s/[^a-zA-Z ]+//g;
$ra->add(split(' ', lc($lorem)));
print $ra->anchor_word(1)->as_string, "\n";
, которые выводят:
\b(?:a(?:dipisicing|liqua|met)|(?:consectetu|tempo)r|do(?:lor(?:emagna)?)?|e(?:(?:li)?t|iusmod)|i(?:ncididunt|psum)|l(?:abore|orem)|s(?:ed|it)|ut)\b
Этот код игнорирует регистр иуважает границы слова.
Я бы порекомендовал написать небольшое приложение на Perl, которое может принимать список слов и использовать этот модуль для вывода строковой версии шаблона регулярных выражений.Вы должны быть в состоянии импортировать этот шаблон в Ruby.Это позволит вам очень быстро найти слова с ошибками.Вы могли бы даже заставить его вывести шаблон в YAML-файл, а затем загрузить этот файл в свой код Ruby.Периодически анализируйте страницы с ошибками в словах, запускайте вывод через код Perl, и ваш код Ruby будет иметь шаблон обновления.
Вы можете использовать этот шаблон для фрагмента текста, просто чтобы увидеть, есть ли слова с ошибками.Если это так, то вы разбиваете текст на предложения или слова и снова проверяете регулярное выражение.Не проверяйте сразу слова, потому что большинство слов будет написано правильно.Это почти как бинарный поиск по вашему тексту - протестируйте все, если есть попадание, а затем разбейте его на более мелкие блоки, чтобы сузить поиск, пока не найдете отдельные орфографические ошибки.То, как вы разбиваете фрагменты, зависит от количества поступающего текстаШаблон регулярного выражения может проверять весь текстовый блок и возвращать нулевое значение или значение индекса, в дополнение к отдельным словам, таким же образом, так что вы получаете большую скорость, выполняя большие куски текста.
Затем, если вы знаете, что у вас есть слово с ошибкой, вы можете выполнить поиск хеша для правильного написания.Это был бы большой хэш, но задача отобрать хорошее и плохое написание - это то, что займет больше всего времени.Поиск будет очень быстрым.
Вот пример кода:
get_words.rb
#!/usr/bin/env ruby
require 'open-uri'
require 'nokogiri'
require 'yaml'
words = {}
['0-9', *('A'..'Z').to_a].each do |l|
begin
print "Reading #{l}... "
html = open("http://en.wikipedia.org/wiki/Wikipedia:Lists_of_common_misspellings/#{l}").read
puts 'ok'
rescue Exception => e
puts "got \"#{e}\""
next
end
doc = Nokogiri::HTML(html)
doc.search('div#bodyContent > ul > li').each do |n|
n.content =~ /^(\w+) \s+ \(([^)]+)/x
words[$1] = $2
end
end
File.open('wordlist.yaml', 'w') do |wordfile|
wordfile.puts words.to_yaml
end
regex_assemble.pl
#!/usr/bin/env perl
use Regexp::Assemble;
use YAML;
use warnings;
use strict;
my $ra = Regexp::Assemble->new('flags' => 'i');
my %words = %{YAML::LoadFile('wordlist.yaml')};
$ra->add(map{ lc($_) } keys(%words));
print $ra->chomp(1)->anchor_word(1)->as_string, "\n";
Запустите первое, а затем запустите второй вывод своих данных в файл для захвата испущенного регулярного выражения.
И еще несколько примеров слов и сгенерированного вывода:
'how now brown cow' => /\b(?:[chn]ow|brown)\b/
'the rain in spain stays mainly on the plain' => /\b(?:(?:(?:(?:pl|r)a)?i|o)n|s(?:pain|tays)|mainly|the)\b/
'jackdaws love my giant sphinx of quartz' => /\b(?:jackdaws|quartz|sphinx|giant|love|my|of)\b/
'fu foo bar foobar' => /\b(?:f(?:oo(?:bar)?|u)|bar)\b/
'ms miss misses missouri mississippi' => /\bm(?:iss(?:(?:issipp|our)i|es)?|s)\b/
Ruby'sRegexp.union
не близко к изощренности Regexp::Assemble
.После захвата списка слов с ошибками, есть 4225 слов, состоящих из 41 817 символов.После запуска Perl Regexp :: Assemble для этого списка было сгенерировано регулярное выражение в 30 954 символа.Я бы сказал, что это эффективно.