辞書に登録されている語をテキスト中から検索する
- ref:裏表(Phinloda のもう裏だか表だか分からないページ) | メモ: パターンマッチのちょっとした問題
- ref:裏表(Phinloda のもう裏だか表だか分からないページ) | メモ: パターンマッチのちょっとした問題 (2)
あー、Computer Science 的にそういうのは AC 法(Aho-Corasick algorithm) で Trie 使ってやるのが正道な気がする。で、一度作った TRIE は保存しておけばよい。
Python なら AC 法を実装した pytst という拡張ライブラリがあるので、これを使えば簡単。Ruby への移植版もある。
import sys import tst def main(args, options): trie = tst.TST() fp = file(options.list_file) try: for line in fp: trie[line.rstrip()] = None finally: fp.close() if len(args) == 0: result = trie.scan(sys.stdin.read(), tst.TupleListAction()) if len(result) > 0: print "stdin match" else: for filename in args: fp = file(filename) result = trie.scan(fp.read(), tst.TupleListAction()) fp.close() if len(result) > 0: print filename if __name__ == '__main__': import optparse parser = optparse.OptionParser() parser.add_option('--list', dest='list_file', type='string') (options, args) = parser.parse_args() main(args, options)
ここでは実行時に TRIE を構築しているが、TST オブジェクトには TRIE の load/save を行う read_from_file, write_to_file というメソッドがあるので、実用的にはそれらを使って構築済みの TRIE をロードしたほうが良い。
C++ なら dartsという TRIE 実装のライブラリがある。
#include <iostream> #include <fstream> #include <string> #include <cstdlib> #include <darts.h> static int check_url(std::istream& in, Darts::DoubleArray& da) { std::string content; std::string line; Darts::DoubleArray::result_pair_type result; while (!in.eof()) { getline(in, line); const char* str = line.c_str(); size_t len = line.length(); for (int i = 0; i < len; i++) { if (da.commonPrefixSearch(str + i, &result, 1, len - i) > 0) { return 1; } } } return 0; } static void usage(const char* progname) { std::cerr << "Usage: " << progname << " double-array [filenames]" << std::endl; exit(1); } int main(int argc, char** argv) { Darts::DoubleArray da; if (argc < 2) { usage(argv[0]); } const char* da_filename = argv[1]; if (da.open(da_filename) != 0) { std::cerr << "failed to load double-array" << std::endl; exit(2); } if (argc == 2) { if (check_url(std::cin, da)) { std::cout << "<stdin>" << std::endl; return 0; } else { return 1; } } else { int retcode = 1; for (int i = 2; i < argc; i++) { const char* filename = argv[i]; std::ifstream in(filename); if (!in) { std::cerr << "failed to open `" << filename << "'" << std::endl; return 2; } if (check_url(in, da)) { std::cout << filename << std::endl; retcode = 0; } } return retcode; } }
こちらは TRIE を使うものの AC 法ではない。TRIE は darts 付属の mkdarts を使って作成しておく。
% sort urls.txt > urls.sorted % mkdarts urls.sorted urls.da
自分は、darts のコードを Java に移植して使ってたりするけど、20万件程度ならさくさく動く。まぁ、固有表現辞書だから URL 見たいに長い文字列じゃないけど。