正規表現
メールアドレスからログイン名を検証して抽出する
メールアドレスが正しい形式であることを検証し、@ 記号より前のすべてを抽出します。
use lazy_static::lazy_static;
use regex::Regex;
fn extract_login(input: &str) -> Option<&str> {
lazy_static! {
static ref RE: Regex = Regex::new(r"(?x)
^(?P<login>[^@\s]+)@
([[:word:]]+\.)*
[[:word:]]+$
").unwrap();
}
RE.captures(input).and_then(|cap| {
cap.name("login").map(|login| login.as_str())
})
}
fn main() {
assert_eq!(extract_login(r"I❤email@example.com"), Some(r"I❤email"));
assert_eq!(
extract_login(r"sdf+sdsfsd.as.sdsd@jhkk.d.rl"),
Some(r"sdf+sdsfsd.as.sdsd")
);
assert_eq!(extract_login(r"More@Than@One@at.com"), None);
assert_eq!(extract_login(r"Not an email@email"), None);
}
テキストから一意な #Hashtags のリストを抽出する
テキストからハッシュタグのリストを抽出し、ソートして重複を排除します。
ここで示すハッシュタグ用の regex は、文字で始まるラテン文字のハッシュタグだけを検出します。完全な twitter hashtag regex は、はるかに複雑です。
use lazy_static::lazy_static;
use regex::Regex;
use std::collections::HashSet;
fn extract_hashtags(text: &str) -> HashSet<&str> {
lazy_static! {
static ref HASHTAG_REGEX : Regex = Regex::new(
r"\#[a-zA-Z][0-9a-zA-Z_]*"
).unwrap();
}
HASHTAG_REGEX.find_iter(text).map(|mat| mat.as_str()).collect()
}
fn main() {
let tweet = "Hey #world, I just got my new #dog, say hello to Till. #dog #forever #2 #_ ";
let tags = extract_hashtags(tweet);
assert!(tags.contains("#dog") && tags.contains("#forever") && tags.contains("#world"));
assert_eq!(tags.len(), 3);
}
テキストから電話番号を抽出する
Regex::captures_iter を使用してテキスト文字列を処理し、複数の電話番号をキャプチャします。ここでの例は、米国式の電話番号を対象としています。
use anyhow::Result;
use regex::Regex;
use std::fmt;
struct PhoneNumber<'a> {
area: &'a str,
exchange: &'a str,
subscriber: &'a str,
}
impl<'a> fmt::Display for PhoneNumber<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "1 ({}) {}-{}", self.area, self.exchange, self.subscriber)
}
}
fn main() -> Result<()> {
let phone_text = "
+1 505 881 9292 (v) +1 505 778 2212 (c) +1 505 881 9297 (f)
(202) 991 9534
Alex 5553920011
1 (800) 233-2010
1.299.339.1020";
let re = Regex::new(
r#"(?x)
(?:\+?1)? # 国番号(任意)
[\s\.]?
(([2-9]\d{2})|\(([2-9]\d{2})\)) # 市外局番
[\s\.\-]?
([2-9]\d{2}) # 交換局コード
[\s\.\-]?
(\d{4}) # 加入者番号"#,
)?;
let phone_numbers = re.captures_iter(phone_text).filter_map(|cap| {
let groups = (cap.get(2).or(cap.get(3)), cap.get(4), cap.get(5));
match groups {
(Some(area), Some(ext), Some(sub)) => Some(PhoneNumber {
area: area.as_str(),
exchange: ext.as_str(),
subscriber: sub.as_str(),
}),
_ => None,
}
});
assert_eq!(
phone_numbers.map(|m| m.to_string()).collect::<Vec<_>>(),
vec![
"1 (505) 881-9292",
"1 (505) 778-2212",
"1 (505) 881-9297",
"1 (202) 991-9534",
"1 (555) 392-0011",
"1 (800) 233-2010",
"1 (299) 339-1020",
]
);
Ok(())
}
複数の正規表現に一致する行でログファイルをフィルタリングする
application.log という名前のファイルを読み込み、“version X.X.X”、
ポート 443 が続く何らかの IP アドレス
(例: “192.168.0.1:443”)、または特定の警告を含む行だけを出力します。
regex::RegexSetBuilder は regex::RegexSet を構成します。
正規表現ではバックスラッシュが非常によく使われるため、raw string literals
を使うと読みやすくなります。
use anyhow::Result;
use std::fs::File;
use std::io::{BufReader, BufRead};
use regex::RegexSetBuilder;
fn main() -> Result<()> {
let log_path = "application.log";
let buffered = BufReader::new(File::open(log_path)?);
let set = RegexSetBuilder::new(&[
r#"version "\d\.\d\.\d""#,
r#"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:443"#,
r#"warning.*timeout expired"#,
]).case_insensitive(true)
.build()?;
buffered
.lines()
.filter_map(|line| line.ok())
.filter(|line| set.is_match(line.as_str()))
.for_each(|x| println!("{}", x));
Ok(())
}
あるテキストパターンのすべての出現箇所を別のパターンに置き換えます。
標準の ISO 8601 の日付パターン YYYY-MM-DD に一致するすべての箇所を、
スラッシュ区切りのアメリカ英語の日付表現に置き換えます。
たとえば 2013-01-15 は 01/15/2013 になります。
メソッド Regex::replace_all は、正規表現全体に一致するすべての箇所を置き換えます。
&str は Replacer トレイトを実装しているため、$abcde のような変数で、
検索用正規表現内の対応する名前付きキャプチャグループ (?P<abcde>REGEX) を参照できます。
例とエスケープの詳細については、replacement string syntax を参照してください。
use lazy_static::lazy_static;
use std::borrow::Cow;
use regex::Regex;
fn reformat_dates(before: &str) -> Cow<str> {
lazy_static! {
static ref ISO8601_DATE_REGEX : Regex = Regex::new(
r"(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})"
).unwrap();
}
ISO8601_DATE_REGEX.replace_all(before, "$m/$d/$y")
}
fn main() {
let before = "2012-03-14, 2013-01-15 and 2014-07-05";
let after = reformat_dates(before);
assert_eq!(after, "03/14/2012, 01/15/2013 and 07/05/2014");
}