NLP 100 Knock section 3
正規表現苦手なようでだいぶ時間がかかりました。
第3章 正規表現
正規表現ときいて楽勝やろと思ってた時期もあったんですが・・・
準備
この章では、gzファイルに入ったJSONをパースする必要があるのでさきにgzファイルから読み込むようにします。
fn read_gzip(path: String) -> Vec<String> {
let mut file = File::open(path).unwrap();
let mut string = String::new();
Decoder::new(&mut file).unwrap().read_to_string(&mut string).unwrap();
string.split("\n").filter(|m| m.ne(&"")).map(|m| m.to_string()).collect::<Vec<String>>()
}
20. JSONデータの読み込み
こいつは、serde
の serde_json
を利用して from_str
で読み込むだけです。
let v: Value = match serde_json::from_str(&l.as_str()) {
Ok(v) => v,
Err(_) => { continue; },
};
21. カテゴリ名を含む行を抽出 \&\& 22. カテゴリ名の抽出
早見表 を参考に Regex::captures で抽出することで解決。
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
extern crate regex;
extern crate serde_json;
use serde_json::Value;
use regex::Regex;
fn main() {
let args: Vec<String> = env::args().skip(1).collect();
let path = &args[0].to_string();
let keywords = &args[1..];
let file = File::open(path).unwrap();
let lines = BufReader::new(file).lines();
let re = Regex::new(r"\[\[Category:.+\]\]").unwrap();
let cap = Regex::new(r"\[\[Category:(?P<name>.+?)(?:\|.*)*\]\]").unwrap();
for l in lines {
let v: Value = serde_json::from_str(&l.unwrap()).unwrap();
let title = &v["title"].as_str().unwrap().to_string();
if keywords.contains(title) {
for content in v["text"].as_str().unwrap().to_string().split("\n").filter(|m| re.is_match(m)) {
for c in cap.captures_iter(content) {
println!("{}", &c["name"]);
}
}
}
}
}
23. セクション構造
こちらは前節の問題と同様に解けばよいですが、=の数 - 1
という罠があります。
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
extern crate regex;
extern crate serde_json;
use serde_json::Value;
use regex::Regex;
fn main() {
let args: Vec<String> = env::args().skip(1).collect();
let path = &args[0].to_string();
let keywords = &args[1..];
let file = File::open(path).unwrap();
let lines = BufReader::new(file).lines();
let re = Regex::new(r"(?P<level>==+)(?P<headline>.+?)(?:=+)").unwrap();
for l in lines {
let v: Value = serde_json::from_str(&l.unwrap()).unwrap();
let title = &v["title"].as_str().unwrap().to_string();
if keywords.contains(title) {
for content in v["text"].as_str().unwrap().to_string().split("\n").filter(|m| re.is_match(m)) {
for caps in re.captures_iter(content) {
println!("level: {}, headline: {}", &caps["level"].len() - 1, &caps["headline"]);
}
}
}
}
}
24. ファイル参照の抽出
このあたりは問題ないとおもいます。
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
extern crate regex;
extern crate serde_json;
use serde_json::Value;
use regex::Regex;
fn main() {
let args: Vec<String> = env::args().skip(1).collect();
let path = &args[0].to_string();
let keywords = &args[1..];
let file = File::open(path).unwrap();
let lines = BufReader::new(file).lines();
let re = Regex::new(r"\[\[(File|ファイル):(?P<filename>.+?)(?:\|.*)*(?:\|.*)*\]\]").unwrap();
for l in lines {
let v: Value = serde_json::from_str(&l.unwrap()).unwrap();
let title = &v["title"].as_str().unwrap().to_string();
if keywords.contains(title) {
for content in v["text"].as_str().unwrap().to_string().split("\n").filter(|m| re.is_match(m)) {
for caps in re.captures_iter(content) {
println!("filename: {}", &caps["filename"]);
}
}
}
}
}
25. テンプレートの抽出
こいつは大変でしたので参考サイト1 を参考に以下の正規表現でとりあえず抽出しています。
let re = Regex::new(r"(?ms)(?:^\{\{基礎情報.*?$)(?P<dict>.+?)(?:^\}\}$)").unwrap();
26. 強調マークアップの除去
こちらも同様に参考サイト2を参考に以下の正規表現で
let strong = Regex::new(r"'{2,5}").unwrap();
27. 内部リンクの除去
こちらも同様に参考サイト3を参考に以下の正規表現で
let link = Regex::new(r"(?:\[{1,2})(?P<link>.+?)(?:\|.+)?(?:\]{1,2})").unwrap();
28. MediaWikiマークアップの除去
どうよう4 (ry
let link = Regex::new(r"(?:\[{1,2})(?P<link>.+?)(?:\|.+)?(?:\]{1,2})").unwrap();
let lang = Regex::new(r"(?:\{\{lang\|.+?\|)(?P<lang>.+?)(?:\}\})").unwrap();
let markup = Regex::new(r"</*.+?>").unwrap();
29. 国旗画像のURLを取得する
こちらもさんこおおう5にしてますが、主にURL部分だけです。 こちらは Hyper を利用することで解決しています。
extern crate futures;
extern crate hyper;
extern crate hyper_tls;
extern crate libflate;
extern crate regex;
extern crate serde_json;
extern crate tokio_core;
use futures::{Future, Stream};
use hyper::Client;
use hyper_tls::HttpsConnector;
use libflate::gzip::Decoder;
use regex::Regex;
use serde_json::Value;
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::Read;
use tokio_core::reactor::Core;
fn get_image(query: String) -> String {
let re = Regex::new(r" ").unwrap();
let uri = format!("https://en.wikipedia.org/w/api.php?action=query&titles=File:{}&format=json&prop=imageinfo&iiprop=url", re.replace_all(&query, "%20"));
let uri = uri.parse().unwrap();
let mut core = Core::new().unwrap();
let handle = core.handle();
let client = Client::configure()
.connector(HttpsConnector::new(4, &handle).unwrap())
.build(&handle);
let work = client.get(uri).and_then(|res| {
res.body().concat2().and_then(move |body| {
let v: Value = serde_json::from_slice(&body).unwrap();
let page_ids = v["query"]["pages"].as_object().unwrap().keys().map(|m| m.to_string()).collect::<Vec<String>>();
let imageinfo = &v["query"]["pages"][&page_ids[0]]["imageinfo"];
let r = format!("{}", imageinfo[0]["url"].as_str().unwrap());
Ok(r)
})
});
core.run(work).unwrap()
}
fn read_gzip(path: String) -> Vec<String> {
let mut file = File::open(path).unwrap();
let mut string = String::new();
Decoder::new(&mut file).unwrap().read_to_string(&mut string).unwrap();
string.split("\n").filter(|m| m.ne(&"")).map(|m| m.to_string()).collect::<Vec<String>>()
}
fn main() {
let args: Vec<String> = env::args().skip(1).collect();
let path = &args[0].to_string();
let keywords = &args[1..];
let lines = read_gzip(path.to_string());
let re = Regex::new(r"(?ms)(?:^\{\{基礎情報.*?$)(?P<dict>.+?)(?:^\}\}$)").unwrap();
let bar_hat = Regex::new(r"(?ms)(?:^\|)").unwrap();
let dict = Regex::new(r"(?P<key>.+?)\s*=\s*(?P<val>.+)").unwrap();
let media = Regex::new(r"\[\[(File|ファイル):(?P<filename>.+?)(?:\|.*)*(?:\|.*)*\]\]").unwrap();
let strong = Regex::new(r"'{2,5}").unwrap();
let link = Regex::new(r"(?:\[{1,2})(?P<link>.+?)(?:\|.+)?(?:\]{1,2})").unwrap();
let lang = Regex::new(r"(?:\{\{lang\|.+?\|)(?P<lang>.+?)(?:\}\})").unwrap();
let markup = Regex::new(r"</*.+?>").unwrap();
for l in lines {
let v: Value = match serde_json::from_str(&l.as_str()) {
Ok(v) => v,
Err(_) => { continue; },
};
let text: String = match v["text"].as_str() {
Some(x) => x.to_string(),
None => { continue; },
};
let title: String = match v["title"].as_str() {
Some(x) => x.to_string(),
None => { continue; },
};
if keywords.contains(&title) {
let mut results: HashMap<String, String> = HashMap::new();
let re: String = match re.captures(&text) {
Some(caps) => caps["dict"].to_string(),
None => {
println!("cannot capture dict!!");
continue;
},
};
for line in bar_hat.split(&re).filter(|f| f.ne(&"")).map(|m| m.to_string()) {
let line = line.replace("\n", "");
let dict = match dict.captures(&line) {
Some(x) => x,
None => {continue;},
};
let val = dict["val"].to_string();
let key = dict["key"].to_string();
let file: String = media.replace_all(&val, "$filename").trim().to_string();
let strong: String = strong.replace_all(&file, "").trim().to_string();
let link: String = link.replace_all(&strong, "$link").trim().to_string();
let markup: String = markup.replace_all(&link, "").trim().to_string();
let val: String = match key.as_ref() {
"国旗画像" => get_image(markup.to_string()),
_ => lang.replace_all(&markup, "$lang").trim().to_string(),
};
results.insert(key, val);
}
for (k, v) in results {
println!("{}: {}", k, v);
}
}
}
}
おわり
とりあえず3章まで終わらせましたので形態素解析へと進みたいとおもいます(思っているだけ)
参考資料
-
素人の言語処理100本ノック:25, segavvy, https://qiita.com/segavvy/items/e402ad0a5b0f52453d7f ↩
-
素人の言語処理100本ノック:26, segavvy, https://qiita.com/segavvy/items/f6d0f3d6eee5acc33c58 ↩
-
素人の言語処理100本ノック:27, segavvy, https://qiita.com/segavvy/items/9a8137f045852bc299d6 ↩
-
素人の言語処理100本ノック:28, segavvy, https://qiita.com/segavvy/items/8c4567ec1124320d3354 ↩
-
素人の言語処理100本ノック:29, segavvy, https://qiita.com/segavvy/items/fc7257012d8a590185e5 ↩