Skip to content

Instantly share code, notes, and snippets.

@kujirahand
Created August 19, 2024 14:42
Show Gist options
  • Save kujirahand/843c34e789af18551c01f22f6cdfab90 to your computer and use it in GitHub Desktop.
Save kujirahand/843c34e789af18551c01f22f6cdfab90 to your computer and use it in GitHub Desktop.
ワイルドカードをRustで実装したもの
fn main() {
// 「ab?.txt」が「abc.txt」にマッチするか --- (*1)
println!("{}", is_match("ab?.txt", "abc.txt")); // true
}
// ワイルドカードのパターン「?」を使った文字列マッチング --- (*2)
fn is_match(pattern: &str, target: &str) -> bool {
// &strをVec<char>に変換してis_match_sliceを呼び出す
let pattern: Vec<char> = pattern.chars().collect();
let target: Vec<char> = target.chars().collect();
is_match_slice(&pattern, &target)
}
// ワイルドカードのパターンを使った文字列マッチング --- (*3)
fn is_match_slice(pattern: &[char], target: &[char]) -> bool {
let mut ip = 0;
let mut it = 0;
// 1文字ずつ順に比較していく --- (*4)
while ip < pattern.len() && it < target.len() {
if pattern[ip] == '*' { // 0文字以上の任意文字にマッチ --- (*5)
ip += 1; // ワイルドカードをスキップ
// ワイルドカードの次の文字が一致するまで繰り返す
for i in it..target.len() {
// 新たなスライスを作って再帰的にマッチングを行う
let sub_pattern = &pattern[ip..];
let sub_target = &target[i..];
if is_match_slice(sub_pattern, sub_target) {
return true;
}
}
continue;
}
if pattern[ip] == '?' { // 任意の1文字にマッチ --- (*6)
ip += 1;
it += 1;
continue;
}
if pattern[ip] == target[it] { // 文字が一致 --- (*7)
ip += 1;
it += 1;
continue;
}
return false; // マッチしない
}
// パターンも対象も末尾まで到達した場合にtrueを返す --- (*8)
ip == pattern.len() && it == target.len()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
// いろいろなパターンを試してみる --- (*9)
assert_eq!(is_match("ab?.txt", "abc.txt"), true);
assert_eq!(is_match("a*.txt", "abc.txt"), true);
assert_eq!(is_match("*.txt", "abc.txt"), true);
assert_eq!(is_match("a?g.txt", "abcdefg.txt"), false);
assert_eq!(is_match("a*g.txt", "abcdefg.txt"), true);
assert_eq!(is_match("a*b*g.txt", "abcdefg.txt"), true);
assert_eq!(is_match("*efg.txt", "abcdefg.txt"), true);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment