6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rust勉強中 - その23 -> テキスト処理と正規表現

Last updated at Posted at 2019-11-01

自己紹介

出田 守と申します。
しがないPythonプログラマです。
情報セキュリティに興味があり現在勉強中です。CTFやバグバウンティなどで腕を磨いています。主に低レイヤの技術が好きで、そっちばかり目が行きがちです。

Rustを勉強していくうえで、読んで学び、手を動かし、記録し、楽しく学んでいけたらと思います。

環境

新しい言語を学ぶということで、普段使わないWindowsとVimという新しい開発環境で行っています。
OS: Windows10 Home 64bit 1903
CPU: Intel Core i5-3470 @ 3.20GHz
Rust: 1.38.0
RAM: 8.00GB
Editor: Vim 8.1.1
Terminal: PowerShell

前回

前回はコレクションについて学びました。
Rust勉強中 - その22

テキスト処理

パターンによる検索と分割

&strやStringに対して検索や分割を行うメソッドには、パターンを引数とするものがあります。
パターンとしては以下のようなものがあります。

  • charは、一文字にマッチ
  • String, &str, &&strは、部分文字列にマッチ
  • FnMut(char) -> boolは、trueを返す一文字にマッチ
  • &[char]は、スライス中の要素それぞれの文字にマッチ

以下はfindメソッドを例です。

fn main() {
    let text = "te\txt\n";
    println!("{:?}", text.find('t'));                      // Some(0)
    println!("{:?}", text.find("xt"));                     // Some(3)
    println!("{:?}", text.find(|e| e=='t'));               // Some(0)
    println!("{:?}", text.find(&['\t', '\n'] as &[char])); // Some(2)
    println!("{:?}", text.find(&['\t', '\n'][..]));        // Some(2)
}

最後の2つは&[char]をパターンとして渡す場合の例です。このとき、パターンを&[char]にas式で変換してあげる必要があります。または&[char][..]を使うこともできます。

フォーマット出力

テンプレート文字列{...}を使って、ストリームにフォーマット出力することができます。
フォーマット出力はマクロを使えば簡単に呼び出すことができます。フォーマット出力に関係する標準マクロには以下のようなものがあります。

  • format!
  • write!
  • writeln!
  • print!
  • println!
  • eprint!
  • eprintln!
  • format_args!

インデックス、名前

テンプレート文字列にインデックスを指定することで埋め込む引数の位置を指定できます。また、名前を指定することで、その名前を指定した引数を埋め込むことができます。

fn main() {
    ...
    println!("{2} {0} {1}", "zero", "one", "two");                     // two, zero, one
    println!("{two} {zero} {one}", zero="zero", one="one", two="two"); // two, zero, one
}

文字列のフォーマット

機能 テンプレート文字列 結果
デフォルト "{}" "text"
最短フィールド幅 "{:5}" "text "
最長テキスト長 "{:.3}" "tex"
左寄せ "{:<6}" "text "
中央寄せ "{:^6}" " text "
右寄せ "{:>6}" " text"
=パディングと中央寄せ "{:=^6}" "=text="
*パディングと中央寄せ "{:=*6}" "*text*"

整数値のフォーマット

機能 テンプレート文字列 結果
デフォルト "{}" "255"
符号 "{:+}" "+255"
最短フィールド幅 "{:4}" " 255"
ゼロパディング "{:04}" "0255"
左寄せ "{:<5}" "255 "
中央寄せ "{:^5}" " 255 "
右寄せ "{:>5}" " 255"
=パディングと中央寄せ "{:=^5}" "=255="
*パディングと中央寄せ "{:*^5}" "*255*"
2進 "{:b}" "11111111"
8進 "{:o}" "377"
16進(小文字) "{:x}" "ff"
16進(大文字) "{:X}" "FF"
16進(基数付き) "{:#x}" "0xff"

浮動小数点数のフォーマット

機能 テンプレート文字列 結果
デフォルト "{}" "12.1858"
最短フィールド幅 "{:8}" " 12.1858"
精度 "{:.2}" "12.19"
ゼロパディング "{:08}" "012.1858"
指数(小文字) "{:e}" "1.21858e1"
指数(大文字) "{:E}" "1.21858E1"

デバッグのフォーマット

機能 テンプレート文字列 結果
デバッグ "{:?}" Point { x: 10, y: 15 }
もっとデバッグ "{:#?}" Point {
  x: 10,
  y: 15,
}

ポインタのフォーマット

機能 テンプレート文字列 結果
ポインタ "{:p}" "0x7ff7e020b800"

動的フィールド幅

インデックスや名前で関数を指定することで、動的に幅を調節することもできます。

fn width(c: &char) -> usize {
    match c {
        'l' => 1,
        'c' => 3,
        'r' => 5,
        _   => 7
    }
}

fn main() {
    ...
    let v = vec!['l', 'r', 'l', 'c', 'r', 'l', 'u', 'l', 'c'];
    for c in &v {
        println!("{:>1$}", c, width(c));
    }
    for c in &v {
        println!("{:>width$}", c, width=width(c));
    }
}
l
    r
l
  c
    r
l
      u
l
  c
l
    r
l
  c
    r
l
      u
l
  c

std::fmt

std::fmt内のフォーマットトレイトを型に実装することで、それぞれ対応するメソッドやマクロが使えるようになります。

トレイト 対応するテンプレート文字列
Binary "{:b}"
Debug "{:?}"
Display "{}"
LowerExp "{:e}"
LowerHex "{:x}"
Octal "{:o}"
Pointer "{:p}"
UpperExp "{:E}"
UpperHex "{:X}"
Write -
use std::fmt;
struct Title {
    title: String
}
impl fmt::Display for Title {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "{}", "*".repeat(self.title.len()))?;
        writeln!(f, "{}", self.title)?;
        writeln!(f, "{}", "*".repeat(self.title.len()))
    }
}

fn main() {
    ...
    println!("{}", Title {title: "Rust".to_string()});
    println!("{}", Title {title: "Rust".to_string()}.to_string());
}
****
Rust
****
****
Rust
****

Displayトレイトを実装すれば自動的にstd::str::ToStringが実装されるため、to_stringメソッドも使えるようになります。

正規表現

正規表現にはregex crateがあります。

Cargo.toml
...
[dependencies]
regex = "1"
main.rs
extern crate regex;
use regex::Regex;

fn main() {
    // new
    let re = Regex::new(r"\d{3}-\d{4}").unwrap();
    // is_match
    println!("{}", re.is_match("000-0000"));
    // find_iter
    for m in re.find_iter("000-0000, 000-0001, 000-0002") {
        println!("{:?}", m);
    }
    // captures_iter
    for m in re.captures_iter("000-0000, 000-0001, 000-0002") {
        println!("{:?}", m);
    }
    // replace_all
    println!("{}", re.replace_all("000-0000, 000-0001, 000-0002", "postcode"));
}
true
Match { text: "000-0000, 000-0001, 000-0002", start: 0, end: 8 }
Match { text: "000-0000, 000-0001, 000-0002", start: 10, end: 18 }
Match { text: "000-0000, 000-0001, 000-0002", start: 20, end: 28 }
Captures({0: Some("000-0000")})
Captures({0: Some("000-0001")})
Captures({0: Some("000-0002")})
postcode, postcode, postcode

詳しくはドキュメントを参照。
ドキュメント - Crate regex 1.3.1

ソース

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32
}

fn width(c: &char) -> usize {
    match c {
        'l' => 1,
        'c' => 3,
        'r' => 5,
        _   => 7
    }
}

use std::fmt;
struct Title {
    title: String
}
impl fmt::Display for Title {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "{}", "*".repeat(self.title.len()))?;
        writeln!(f, "{}", self.title)?;
        write!(f, "{}", "*".repeat(self.title.len()))
    }
}

fn main() {
    // pattern
    let text = "te\txt\n";
    println!("{:?}", text.find('t'));                      // Some(0)
    println!("{:?}", text.find("xt"));                     // Some(3)
    println!("{:?}", text.find(|e| e=='t'));               // Some(0)
    println!("{:?}", text.find(&['\t', '\n'] as &[char])); // Some(2)
    println!("{:?}", text.find(&['\t', '\n'][..]));        // Some(2)
    // format index, name
    println!("{2} {0} {1}", "zero", "one", "two");                     // two, zero, one
    println!("{two} {zero} {one}", zero="zero", one="one", two="two"); // two, zero, one
    // format text
    println!("{}",    "text");  // "text"
    println!("{:5}",  "text");  // "text "
    println!("{:.3}", "text");  // "tex"
    println!("{:<6}", "text");  // "text  "
    println!("{:^6}", "text");  // " text "
    println!("{:>6}", "text");  // "  text"
    println!("{:=^6}", "text"); // "=text="
    println!("{:*^6}", "text"); // "*text*"
    // format integer
    println!("{}",     255); // "255"
    println!("{:+}",   255); // "+255"
    println!("{:4}",   255); // " 255"
    println!("{:04}",  255); // "0255"
    println!("{:<5}",  255); // "255  "
    println!("{:^5}",  255); // " 255 "
    println!("{:>5}",  255); // "  255"
    println!("{:=^5}", 255); // "=255="
    println!("{:*^5}", 255); // "*255*"
    println!("{:b}",   255); // "11111111"
    println!("{:o}",   255); // "377"
    println!("{:x}",   255); // "ff"
    println!("{:X}",   255); // "FF"
    println!("{:#x}",  255); // "0xff"
    // format float
    println!("{}",    12.1858); // "12.1858"
    println!("{:8}",  12.1858); // " 12.1858"
    println!("{:.2}", 12.1858); // "12.19"
    println!("{:08}", 12.1858); // "012.1858"
    println!("{:e}",  12.1858); // "1.21858e1"
    println!("{:E}",  12.1858); // "1.21858E1"
    // format debug
    println!("{:?}",  Point {x: 10, y: 15}); // Point { x: 10, y: 15 }
    println!("{:#?}", Point {x: 10, y: 15}); // Point {
                                             //    x: 10,
                                             //    y: 15,
                                             // }
    // format pointer
    println!("{:p}", &18); // 0x7ff7e020b800
    // dynamic width
    let v = vec!['l', 'r', 'l', 'c', 'r', 'l', 'u', 'l', 'c'];
    for c in &v {
        println!("{:>1$}", c, width(c));
    }
    for c in &v {
        println!("{:>width$}", c, width=width(c));
    }
    // implement
    println!("{}", Title {title: "Rust".to_string()});
    println!("{}", Title {title: "Rust".to_string()}.to_string());
}
Some(0)
Some(3)
Some(0)
Some(2)
Some(2)
two zero one
two zero one
text
text
tex
text
 text
  text
=text=
*text*
255
+255
 255
0255
255
 255
  255
=255=
*255*
11111111
377
ff
FF
0xff
12.1858
 12.1858
12.19
012.1858
1.21858e1
1.21858E1
Point { x: 10, y: 15 }
Point {
    x: 10,
    y: 15,
}
0x7ff6f65a2210
l
    r
l
  c
    r
l
      u
l
  c
l
    r
l
  c
    r
l
      u
l
  c
****
Rust
****
****
Rust
****

今回はここまでー!

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?