スクリプト言語を使ってバイナリのデータを解析して不要な箇所を削除したい
解決済
回答 1
投稿
- 評価
- クリップ 0
- VIEW 4,471
とある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データを抜き出し復元したい(パケットのロスや重複は考えないものとします)
[手順]
- バイナリデータを先頭から見ていき"DCAA"が出るまで読み飛ばす
- "DCAA"が出てきたらcommand_idを見て0202Hであるか確認する(それ以外のコマンドが持っているdataはJpegではない)
- 0202Hであれば identifier~length を削除する
- 0202H以外であれば identifier~data を削除する
以下ファイルの終端まで繰り返し
これをperlで実現しようと思っています。
MAIN:{
my $buf, $size;
#ファイルオープン
open ( IN, "< $_$ARGV[0]" ) or die "$!"; # in
open ( OUT, "> $ARGV[1]]" ) or die "$!"; # out
binmode IN;
binmode OUT;
$size = -s $_$ARGV[0];
read(IN, $buf, $size);
my $identifier = "DCAA";
# 16進バイナリ から 文字列 への変換
my @ascii = unpack('H*', $buf);
for my $i (0..$#ascii){
if ( $#ascii eq $identifier ){
# ....
}
}
ここまでやりました。
教えていただきたい事は
+ バイナリデータとアスキーデータの比較
+ バイナリデータの削除(これはバイナリ出力時に読み飛ばせばいいかな)
+ バイナリデータのシーク
の3つを教えてください。
よろしくお願いします。
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
0
unpackしないほうが楽そうだったので、readで少しずつ読みながら基本バイナリのままで処理してみました。
エラー処理など細かいところは省略してます。
なお、シークするにはそのままseek
という関数があるのでこれでシークできます(以下のコードでは使ってませんが)。
use strict;
{
open(my $in, "<:raw", $ARGV[0]) or die $!;
# DCAAまで読み飛ばす
my $last_4byte;
read($in,$last_4byte,4); #readの戻り値チェックは省略
while( $last_4byte ne "DCAA" ){
# 1byte捨てて1byte読む
$last_4byte = substr($last_4byte,1);
if ( read($in,$last_4byte,1,3) < 1 ) {
die "Not found.";
}
}
# resultとcommand_id、command_idチェック
my $result;
my $command_id;
read($in,$result,2);
read($in,$command_id,2);
if ( $command_id ne "\x02\x02" ) {
die "No JPEG."; #読み飛ばしは省略、後ろで$data捨てれば良い
}
# length
my $length;
read($in,$length,4);
my $length_val = unpack("N", $length); # 符号無し4バイト整数、ビッグエンディアン
# data
my $data;
read($in,$data,$length_val);
#必要なところだけ出力
open(my $out, ">:raw", $ARGV[1]) or die $!;
print $out $data;
close($out);
#繰り返し省略
close($in);
}
それから、この部分ですが、
# 16進バイナリ から 文字列 への変換
my @ascii = unpack('H*', $buf);
このコメントは不正確で、正しくはバイナリを16進数文字列に変換します(1バイトが16進数2文字になる)。
要素数は1になるので、後ろのループは1回しか回らないですね。
my $hex_str = unpack('H*', "123");
if ( $hex_str eq "313233" ) {
print "Match!\n";
}
#これだと各バイトを数値にした配列が得られる
my @bytes = unpack('C*', "123");
if ( $bytes[0] == 0x31 && $bytes[1] == 0x32 && $bytes[2] == 0x33 ) {
print "Match!\n";
}
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 89.97%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2016/03/31 19: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.";
}
}
```
で読み取りが飛ばされる事がある気がしてましてそれを検証しています。
本当なら整形する元データを提示したいのですが、申し訳ないです。
また分かりしだいコメントを書かせてください。
2016/04/02 01:42
読み飛ばしてると感じたのはこちらの確認ミスでした。
改めてもう一度やり直したら正しくデータを取得することが出来ました。
本当にありがとうございました。