質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

Q&A

解決済

3回答

3559閲覧

RustでString型をbytesに変換するには?

kphex

総合スコア42

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

0グッド

0クリップ

投稿2019/08/02 08:28

以下の様にrの文字列型を受け取った後:

let mut r = String::new(); // some functions and returns new_data : &[u8] r = String::from_utf8(Vec::from(new_data)).unrwap(); println!("result is {}", r); // "0x148de18c21abe5c1ea727e3204ccbb4cad5f4a9da758cd957f5a6d19454d47b1cb25790cfedc643d1ab730bff97c1d4283da3faacd96b7df0bfb7bdcf3d4d029"

再度このrの値をprimitive_typesクレートを使い、64bytes型に変換して受け渡しをしたいのですが、なぜか以下の様にするとエラーになってしまいます。

crate primitive_types::H512; // 中略 let b = H512::from_slice(&r.as_bytes);
thread 'main' panicked at 'assertion failed: `(left == right)` left: `132`, right: `64`', /Users/hogehoge/.cargo/registry/src/github.com-1ecc6299db9ec823/primitive-types-0.3.0/src/lib.rs:68:1 note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

どなたか方法をご教授頂けますとさいわいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

r"0x148de18c.."という16進数の文字列ですが、これは0, x, 1, 4といった文字をUTF-8形式で表現したものです。str::as_bytes()はそのUTF-8表現のバイト列を返すメソッドですので、0, x, 1, 4なら、個々の 文字コード に対応する&[0x30, 0x78, 0x31, 0x34, ..]が返されます。

このことを説明するために、str::as_bytes()で得られた&[u8]の各要素を16進数で表示するコードを書いてみました。

rust

1fn hexdump(bytes: &[u8]) { 2 println!("{} bytes:", bytes.len()); 3 for (i, b) in bytes.iter().enumerate() { 4 // b: &u8 の値を2桁の16進数で表示する 5 print!("{:02.x} ", b); 6 7 // 値を16個表示するごとに改行する 8 if (i + 1) % 16 == 0 { 9 println!(); 10 } 11 } 12 println!(); 13} 14 15let r = "0x148de18c21abe5c1ea727e3204ccbb4cad5f4a9da758cd957f5a6d19454d47b1cb25790cfedc643d1ab730bff97c1d4283da3faacd96b7df0bfb7bdcf3d4d029".to_string(); 16let bytes = r.as_bytes(); 17hexdump(bytes);

実行すると以下のように表示されます。

plain

1130 bytes: 230 78 31 34 38 64 65 31 38 63 32 31 61 62 65 35 363 31 65 61 37 32 37 65 33 32 30 34 63 63 62 62 434 63 61 64 35 66 34 61 39 64 61 37 35 38 63 64 539 35 37 66 35 61 36 64 31 39 34 35 34 64 34 37 662 31 63 62 32 35 37 39 30 63 66 65 64 63 36 34 733 64 31 61 62 37 33 30 62 66 66 39 37 63 31 64 834 32 38 33 64 61 33 66 61 61 63 64 39 36 62 37 964 66 30 62 66 62 37 62 64 63 66 33 64 34 64 30 1032 39

一方、H512::from_slice()が期待しているのは、文字コードの並びではなく、&[0x14, 0x8d, 0xe1, 0x8c, ..]といった数値データの並びになります。つまりstr::as_bytes()ではなく、16進数の文字列を数値データの並びへと変換する関数が必要です。

そのような関数は標準ライブラリのメソッドを使って簡単に自作できると思いますが、すでにhexというクレートがあるのでそれを使う方法を紹介します。

先ほどのコードをhexクレートで書き換えると以下のようになります。

rust

1// Cargo.tomlに以下を追加 2// [dependencies] 3// hex = "0.3" 4 5use hex; 6 7let r = "0x148de18c21abe5c1ea727e3204ccbb4cad5f4a9da758cd957f5a6d19454d47b1cb25790cfedc643d1ab730bff97c1d4283da3faacd96b7df0bfb7bdcf3d4d029".to_string(); 8// 先頭に"0x"があるとhexクレートでデコードできないので削除する 9let r = r.replacen("0x", "", 1); 10// 16進数文字列をhexクレートでデコードする 11let bytes = hex::decode(&r).unwrap(); // Vec<u8>型 12hexdump(&bytes);

plain

164 bytes: 214 8d e1 8c 21 ab e5 c1 ea 72 7e 32 04 cc bb 4c 3ad 5f 4a 9d a7 58 cd 95 7f 5a 6d 19 45 4d 47 b1 4cb 25 79 0c fe dc 64 3d 1a b7 30 bf f9 7c 1d 42 583 da 3f aa cd 96 b7 df 0b fb 7b dc f3 d4 d0 29

期待通りの値になったようです。

このように16進数文字列をhex::decode()でデコードしてから、H512::from_slice()に渡してみてください。



追記 2019-08-07

最後に恐縮ですが、この辺りの理解を深める為のリソースでオススメ等ございましたら後学の為に共有頂けると助かります。

質問者さまがどの程度の知識をお持ちなのかわからないので、理解を深めるために必要になりそうな項目を全て列挙します。すでにご存知のものについては無視してください。


整数と文字列の内部表現(メモリ上でのデータ形式)の違いについて確認する

整数の内部表現

  • 2進法と16進法(1バイトの情報はちょうど16進数2桁で表せる)
  • 8ビット整数、32ビット整数、64ビット整数など、加減乗除などの演算をCPUがサポートしている形式
    • Rustならu8, i32, usize型などのスカラ型
  • 多バイト長整数(または多倍長整数) ・・・512ビット(64バイト)整数など、演算をCPUがサポートしていない形式
    • バイト列で表現することが多い。(RustならVec<u8>など)
    • Rustではたとえばnum-bigintクレートが可変長の多バイト長整数を提供する。primitive-typesクレートはEthereumなどで使われている固定長の多バイト長整数を提供する。(可変長:整数の大きさによってバイト数が変わる、固定長:バイト数が固定されている)
  • バイトの並び順 ・・・リトルエンディアンとビッグエンディアン

文字列の内部表現(文字コードとエンコーディング)

  • Rustの文字列型(strString)はUTF-8エンコーディングを使用している
  • 整数と文字列では内部表現が異なる
    • たとえば32ビット符号付き整数(Rustではi32型)の-42と、文字列(Rustでは&strまたはString型)の"-42"ではメモリ上に異なる情報が格納される
    • Rustで文字列"-42"i32型の-42に変換するには、str型のparseメソッドを使う(例:"-42".parse::<i32>()
    • その逆の変換をするにはToStringトレイトのto_stringメソッドや、format!マクロを使う

内部表現から外部表現への変換と、その逆の変換について確認する

  • 外部表現はデータをファイルに保存したり、他のプログラムとデータをやりとりするときに必要となる
  • 外部表現にはバイナリデータ(println!"{}"などで表示できない生のバイト列)とテキストデータ(表示可能な文字で構成された文字列)がある

たとえばJSON形式

  • JSONは32ビット整数や文字列などは表現できるが、多バイト長整数のようなデータは直接表現できない
  • JSONはバイナリデータを扱えない
  • 多バイト長整数のようなデータはテキストデータに変換(エンコード)する必要がある

エンコード形式には、16進数文字列や、Base64などがよく使われる

  • 前者よりも後者の方が変換後のデータサイズが小さい
  • 一般的にハッシュデータは前者、それ以外は後者が好んで使われるが、特に決まりがあるわけではない

これらの概念についてはRustに特化した包括的な説明はなさそうなので、「コンピュータにおける情報の表現」をC言語などを使って解説しているサイトを見るのがいいのかなと思います。たとえば基本的な部分はhttps://www.seiai.ed.jp/sys/text/cs/index.htmlのようなサイトを参照して、そこにない情報(多バイト長整数、16進数文字列、Base64エンコーディングなど)については、それぞれ検索して、出てきた記事を読んでみるといったイメージです。

もしこれらの概念についてはすでに理解されていて、Rustではどう扱えばいいのかだけを知りたいのなら、(宣伝になってしまいますが)たとえば私が共著で執筆した書籍で文字列などについては少し詳しく解説していますので参考になるかもしれません。今年の5月に発売されたばかりの書籍ですので、大きな書店なら置いてあると思います。

実践Rust入門(技術評論社) ISBN978-4-297-10559-4

  • 4-2-3 固定精度の整数 ・・・内部表現の説明はないのですが、桁あふれの扱いなどを説明しています
  • 4-2-5 文字 ・・・Unicodeスカラ値について説明しています
  • 4-3-4 文字列スライス(str型) ・・・UTF-8エンコーディングなどもごく簡単にですが説明しています
  • 5-2-4 String ・・・to_string()parse()Stringstrの比較(図で説明)、など
  • 7-9 ライフタイムの詳細:簡単なベクタの実装 ・・・Stringの内部表現にも使われているVec<T>を真似た型を実装することで、Rustにおける所有権の概念に加えて、データの内部表現についても学べます

書籍の概要については共著者のκeenさんが書かれたブログ記事が参考になると思います。

実践Rust入門を書いたよ

投稿2019/08/05 10:50

編集2019/08/07 00:00
tatsuya6502

総合スコア2035

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kphex

2019/08/05 14:40

ご丁寧に有り難う御座いました。 16進数の文字列と文字コードについて理解が足りていなかったので大変助かりました。 ご指摘頂きました通り、`r`の値を`hex::decode()`後に`println!`してみた所: ``` "a6059ac4d999dfb587893ba6059dc2c5c4b2a38b028a9d34c4e3a0e9d0cf87417c98bbc7c375637567eaa59d5fe41774fa7ff33e06e41c623c4d72799d4bea61" // r.replacen("0x", "", 1)後の値のprintln! thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: InvalidHexCharacter { c: '\"', index: 0 }', src/libcore/result.rs:999:5 // bytes = hex::decode(&r).unwrap() 後のprintln! note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace. ``` とこちら`InvalidHexCharacter`エラーが帰ってきてしまいました。 `r`に関しての処理にまだ足りない部分があるのでしょうか?
tatsuya6502

2019/08/05 22:57 編集

{ c: '\"', index: 0 } なので、インデックスが0の文字(つまりrの最初の文字)がダブルクオーテション(")になっているようです。また、println!の結果によると、rの最後の文字もダブルクオーテションのようです。 rに含まれる全てのダブルクオーテションを削除するために、replace()のところを以下のように変更してみてください。 let r = r.replace("\"", "").replacen("0x", "", 1);
kphex

2019/08/06 01:07

素早いご返答有り難う御座いました。 無事にこちら問題なく64bytesの数値データへとデコードする事ができました。 最後に恐縮ですが、この辺りの理解を深める為のリソースでオススメ等ございましたら後学の為に共有頂けると助かります。
tatsuya6502

2019/08/07 00:04

> 最後に恐縮ですが、この辺りの理解を深める為のリソースでオススメ等ございましたら後学の為に共有頂けると助かります。 返信を書き始めたところ思いのほか長くなったので、回答の方に追記しました。(返信の内容がご期待に沿えているのかはちょっと自信ありません)
kphex

2019/08/07 00:19

度々のご回答有り難うございます。 今まで強い型付けに関してはぼんやりとしか扱って居なかったのですが、これをきっかけにきちんと学習したいと強く思いました。
guest

0

If the length of src and the number of bytes in Self do not match.

該当部分のpanicsを見ると、パラメータのデータ長が合わない場合に発生するようですね。

https://docs.rs/primitive-types/0.3.0/primitive_types/struct.H512.html#method.from_slice

投稿2019/08/05 10:14

Yajamon

総合スコア88

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kphex

2019/08/05 10:25

有り難う御座いました。 `String`型で出力してみた場合、512bit, 64bytes(符号を除く)となるのでその辺でしょうか?
kphex

2019/08/05 10:26

先の方の回答にもあるように、64バイトのスライスを直接`H512::from_slice`に渡しても同様のエラーになってしまうのは何故でしょうか?
guest

0

使ったことのないクレートなので間違えていたら申し訳ないですが、ドキュメントを読む限りprimitive_types::H512::from_slice は64バイトの&[u8]スライスが渡されることを想定しているのだと思います。

その一方で、new_dataはそのスライスそのものでなく、何らかの64バイトのバイナリ列の16進数表示(を引用符で囲んだもの)の文字列のように見えます。もしその元となったスライスなどがあるのでしたら、それをそのままH512::from_sliceに渡してみてはいかがでしょうか。

投稿2019/08/02 09:20

tesaguri

総合スコア33

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kphex

2019/08/05 07:50

ご返答有り難うございます。 ためしてみた結果、やはり同じエラーになってしまいました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問