目的
入力されたテキストを正規表現などでマッチさせたいときに、元々のテキストの表記ゆれを無くしてからチェックしたいです。
Unicode正規化
特に表記ゆれの無くし方に指定が無い場合はUnicodeの正規化を使えばよいです。
[dependencies]
unicode-normalization = "0.1.20"
use unicode_normalization::UnicodeNormalization;
fn main() {
let src = "A1#@(アガー";
let c = src.nfkc().collect::<String>();
println!("{}",src);
println!("{}",c);
}
A1#@(アガー
A1#@(アガー
しかし細かい要件があった
今回は以下のような要件がありました。
- 英数字 半角
- 一部記号(#@) 半角
- 半角カタカナ(括弧等の記号も含む) 全角
上記サンプルでは全角左括弧が半角になってしまって要件を満たさないです。
色々なcrateを調査
以下のcrateを試してみました。
半角カナだけ変換などの部分的なものはありましたが、記号を一部だけ変換する機能がなくてぴったりとは収まりませんでした。
charに分解して変換
入力文字列をcharに分解して処理することを考えました。
use std::collections::HashMap;
fn main() {
let mut map: HashMap<char, char> = HashMap::new();
map.insert('A', 'A');
map.insert('1', '1');
map.insert('#', '#');
map.insert('@', '@');
map.insert('ア', 'ア');
map.insert('ー', 'ー');
let src = "A1#@(アガー";
let dst = src.chars().map(|c| {
//println!("{:?}", c);
match map.get(&c) {
Some(v) => *v,
None => c
}
}).collect::<String>();
println!("{}",src);
println!("{}",dst);
}
A1#@(アガー
A1#@(アガー
はい、要件を一つ満たしていないです。「ガ」が全角になっていません。
map.insert('ガ', 'ガ');
を入れればいいじゃないと思うじゃないですか。これコンパイルエラーになります。
error: character literal may only contain one codepoint
一文字にみえるのですが、実は2文字で構成されています。
「'カ'」「'\u{ff9e}'」
です。
困りました。mapをcharからstrにするのは簡単ですがcharsをどうにかする必要があります。なんとか「ガ」を一文字として認識してほしいのです。
unicode-segmentation
ありました。
unicode-segmentation
完成したコード
[dependencies]
unicode-segmentation = "1.9.0"
use std::collections::HashMap;
use unicode_segmentation::UnicodeSegmentation;
fn main() {
let mut map: HashMap<&'static str, &'static str> = HashMap::new();
map.insert("A", "A");
map.insert("1", "1");
map.insert("#", "#");
map.insert("@", "@");
map.insert("ア", "ア");
map.insert("ー", "ー");
map.insert("ガ", "ガ");
let src = "A1#@(アガー";
let dst = src.graphemes(true).map(|c| {
//println!("{:?}", c);
match map.get(c) {
Some(v) => *v,
None => c,
}
}).collect::<String>();
println!("{}",src);
println!("{}",dst);
}
A1#@(アガー
A1#@(アガー
まとめ
細かい要件がある場合、必要な変換をHashMapに定義しておけば対応できるようになりました。
また複数の絵文字が合体したような絵文字も1文字として扱ってくれるので、絵文字をごにょごにょしたい時は便利そうです。