自己紹介
出田 守と申します。
しがない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があります。
...
[dependencies]
regex = "1"
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
****
今回はここまでー!