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

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

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

Perlは多目的に使用される実用性が高い動的プログラミング言語のひとつです。

Q&A

解決済

1回答

8234閲覧

スクリプト言語を使ってバイナリのデータを解析して不要な箇所を削除したい

izuki_y

総合スコア65

Perl

Perlは多目的に使用される実用性が高い動的プログラミング言語のひとつです。

0グッド

0クリップ

投稿2016/03/30 14:40

とあるJpegの送信を行ったTCP通信時のバイナリデータがあります。
そこから不要な情報を削除してJpegデータを復元したいと思っています。
条件は以下の通りです。

[前提条件]
以下のヘッダにJpegを分割してくっ付けて送っています。

identifier[4 byte] = "DCAA"
result[2 byte]
command_id[2 byte] = 0001H~02FFH
length[4 byte] = dataサイズ
data[n byte] = Jpegデータ

ここから分割したJpegデータを抜き出し復元したい(パケットのロスや重複は考えないものとします)

[手順]

  1. バイナリデータを先頭から見ていき"DCAA"が出るまで読み飛ばす
  2. "DCAA"が出てきたらcommand_idを見て0202Hであるか確認する(それ以外のコマンドが持っているdataはJpegではない)
  3. 0202Hであれば identifier~length を削除する
  4. 0202H以外であれば identifier~data を削除する

以下ファイルの終端まで繰り返し

これをperlで実現しようと思っています。

perl

1MAIN:{ 2 my $buf, $size; 3 4 #ファイルオープン 5 open ( IN, "< $_$ARGV[0]" ) or die "$!"; # in 6 open ( OUT, "> $ARGV[1]]" ) or die "$!"; # out 7 8 binmode IN; 9 binmode OUT; 10 11 $size = -s $_$ARGV[0]; 12 13 read(IN, $buf, $size); 14 15 my $identifier = "DCAA"; 16 17 # 16進バイナリ から 文字列 への変換 18 my @ascii = unpack('H*', $buf); 19 for my $i (0..$#ascii){ 20 if ( $#ascii eq $identifier ){ 21 # .... 22 } 23 }

ここまでやりました。

教えていただきたい事は

  • バイナリデータとアスキーデータの比較
  • バイナリデータの削除(これはバイナリ出力時に読み飛ばせばいいかな)
  • バイナリデータのシーク

の3つを教えてください。
よろしくお願いします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

unpackしないほうが楽そうだったので、readで少しずつ読みながら基本バイナリのままで処理してみました。
エラー処理など細かいところは省略してます。
なお、シークするにはそのままseekという関数があるのでこれでシークできます(以下のコードでは使ってませんが)。

Perl

1use strict; 2{ 3 open(my $in, "<:raw", $ARGV[0]) or die $!; 4 5 # DCAAまで読み飛ばす 6 my $last_4byte; 7 read($in,$last_4byte,4); #readの戻り値チェックは省略 8 while( $last_4byte ne "DCAA" ){ 9 # 1byte捨てて1byte読む 10 $last_4byte = substr($last_4byte,1); 11 if ( read($in,$last_4byte,1,3) < 1 ) { 12 die "Not found."; 13 } 14 } 15 16 # resultとcommand_id、command_idチェック 17 my $result; 18 my $command_id; 19 read($in,$result,2); 20 read($in,$command_id,2); 21 if ( $command_id ne "\x02\x02" ) { 22 die "No JPEG."; #読み飛ばしは省略、後ろで$data捨てれば良い 23 } 24 25 # length 26 my $length; 27 read($in,$length,4); 28 my $length_val = unpack("N", $length); # 符号無し4バイト整数、ビッグエンディアン 29 30 # data 31 my $data; 32 read($in,$data,$length_val); 33 34 #必要なところだけ出力 35 open(my $out, ">:raw", $ARGV[1]) or die $!; 36 print $out $data; 37 close($out); 38 39 40 #繰り返し省略 41 42 close($in); 43}

それから、この部分ですが、

Perl

1# 16進バイナリ から 文字列 への変換 2my @ascii = unpack('H*', $buf);

このコメントは不正確で、正しくはバイナリを16進数文字列に変換します(1バイトが16進数2文字になる)。
要素数は1になるので、後ろのループは1回しか回らないですね。

Perl

1my $hex_str = unpack('H*', "123"); 2if ( $hex_str eq "313233" ) { 3 print "Match!\n"; 4} 5 6#これだと各バイトを数値にした配列が得られる 7my @bytes = unpack('C*', "123"); 8if ( $bytes[0] == 0x31 && $bytes[1] == 0x32 && $bytes[2] == 0x33 ) { 9 print "Match!\n"; 10}

投稿2016/03/30 16:32

YsMana

総合スコア257

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

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

izuki_y

2016/03/31 10:57

回答ありがとうございます。 unpackを使わない条件比較の方法など大変参考になりました。 今は教えていただいた事を参考にしながら組んでいます。 ``` while( $last_4byte ne "DCAA" ){ # 1byte捨てて1byte読む $last_4byte = substr($last_4byte,1); if ( read($in,$last_4byte,1,3) < 1 ) { die "Not found."; } } ``` で読み取りが飛ばされる事がある気がしてましてそれを検証しています。 本当なら整形する元データを提示したいのですが、申し訳ないです。 また分かりしだいコメントを書かせてください。
izuki_y

2016/04/01 16:42

失礼な事を言って申し訳ありませんでした。 読み飛ばしてると感じたのはこちらの確認ミスでした。 改めてもう一度やり直したら正しくデータを取得することが出来ました。 本当にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問