バイナリファイルを読み込み、バイナリデータを2BYTEずつ配置交換して、
ファイルに書き込むようなことをしたいのですが、
どなたかご教授よろしくお願い致します。
配置交換前)
FC 34 53 24 65 B5
44 FF FF 45 89 00
↓
配置交換後)
34 FC 24 53 B5 65
FF 44 45 FF 00 89
以上
「やりたいこと」はわかりますが……。
> どなたかご教授よろしくお願い致します。
「何を」教授して欲しいのですか?
「何が」わからないのかがはっきりしないのに,
物事を説明することは難しいです。
バイナリファイルの読み書きがわからないのか?
配置変換がわからないのか?
全部わからないのか?
更に環境は?
JUN2さん YuOさん
おっしゃることはごもっともです。
ご指摘ありがとうございます。
読み込んだファイルをbufに取り込むところまで
実現できたのですけど。
bufの先頭から2BYTEずつ取り出し、変換する処置がまったく
わからず困っております。
以上です。
まず,値の入れ替えの方法。
入れ替えは,
・aの値を他の変数に保存
・bの値をaに代入
・bに元のaの値を代入
という手順でできます。
あとは,bufをunsigned charの配列と見なして,
buf[2 * i]とbuf[2 * i + 1]を入れ替えます。
YuOさん ご返答ありがとうございます。
半日ガンバってみましたが、結果は・・・(汗)
もう少しガンバってからソースを掲載したいと思います。
お世話になっております。
KENです。
サイトを駆け巡りまして一応動作するようになりました。
YuOさんのおっしゃっていたunsigned charについては、
すいません・・・ 何が何だか理解ができなかったです。
C++初めて1週間なので・・・申し訳ございません。。。
アルゴリズムについては参考にさせて頂きました。
ありがとうございます。
一応動くようにはなりましたけど、ここで質問があります。
ファイルサイズからバッファサイズを指定したいのですけど
どのようにしたら良いのでしょうか?
BUFFER_SIZEを20MBで指定して実行を行うとアドレスエラー?
らしきものが出て困っております。
実際の運用では20MBファイルを扱うのでどうにかして
バッファのサイズを可変したいと思っております。
ご教授よろしくお願い致します。
以下がソースコードです。
-------------------------------------
#include <windows.h>
#include "BigEndian.h"
#define BUFFER_SIZE 100000 //100KB ← ココの部分
DWORD BigEndian(LPCTSTR lpFileName1)
{
HANDLE hFile1, hFile2;
DWORD dwRead1;
DWORD dwFileSize1, dwFileSize2;
DWORD i;
BYTE FileData[BUFFER_SIZE];
BYTE A[1], B[1];
hFile1 = CreateFile( lpFileName1,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if( hFile1 == INVALID_HANDLE_VALUE ){
CloseHandle( hFile1 );
return NOT_EXIST_FILE1;
}
hFile2 = CreateFile( "test.bin",
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL );
if( hFile2 == INVALID_HANDLE_VALUE ){
CloseHandle( hFile1 );
CloseHandle( hFile2 );
return NOT_EXIST_FILE2;
}
dwFileSize1 = GetFileSize( hFile1, NULL );
dwFileSize2 = GetFileSize( hFile2, NULL );
/* ファイル読み込み */
ReadFile( hFile1, &FileData, BUFFER_SIZE, &dwRead1, NULL );
/* ビックエンディアン */
for( i=0; i < BUFFER_SIZE / 8; i++ ){
A[0] = FileData[2*i];
B[0] = FileData[2*i+1];
FileData[2*i] = B[0];
FileData[2*i+1] = A[0];
}
/* ファイル書き込み */
WriteFile( hFile2, &FileData, BUFFER_SIZE, &dwRead1, NULL );
CloseHandle( hFile1 );
CloseHandle( hFile2 );
return SUCCESS;
}
なんか難しく考えすぎのような気がしますが
do {
int c1=getc(infp);
int c2=getc(infp);
putc(c2, outfp);
putc(c1, outfp);
} while (c1!=EOF && c2!=EOF);
# 上記は EOF の扱いがバグっています。要修正。
でOK。バッファなど不要。
> 一応動くようにはなりましたけど、ここで質問があります。
> ファイルサイズからバッファサイズを指定したいのですけど
> どのようにしたら良いのでしょうか?
メモリの確保・解放を自分で行うことでできます。
malloc/freeとか,new[]/delete[]とか,HeapAlloc/HeapFreeとか。
C++使っているならstd::vectorが楽です。
ただ,今回のような場合はファイルマッピングを使って処理した方が簡単だと思います。
> BUFFER_SIZEを20MBで指定して実行を行うとアドレスエラー?
> らしきものが出て困っております。
スタックをオーバーしたのでしょう。
VC++のスタックは1MBなので,20MBの自動変数を確保することはできません。
ファイル全体を一度に読み込もうとせず,
何回かに分けて読み込むと良いです。
> if( hFile1 == INVALID_HANDLE_VALUE ){
> CloseHandle( hFile1 );
> return NOT_EXIST_FILE1;
> }
INVALID_HANDLE_VALUEをCloseHandleで閉じる必要はないですし,閉じてはいけません。
INVALID_HANDLE_VALUEはあくまでinvalidなハンドルです。
> dwFileSize1 = GetFileSize( hFile1, NULL );
せっかくファイルサイズを得たのに,
> /* ファイル読み込み */
> ReadFile( hFile1, &FileData, BUFFER_SIZE, &dwRead1, NULL );
BUFFER_SIZEで読み込むのは何故?
#まぁ,BUFFER_SIZE > dwFileSize1であっても問題はないのだが……。
それから,書き込む側のファイルサイズを取得するのは無駄です。
CREATE_ALWAYSした直後ですから,常に0バイトです。
> /* ビックエンディアン */
/* ビッグエンディアン */
> for( i=0; i < BUFFER_SIZE / 8; i++ ){
BUFFER_SIZEで回すのではなく,dwRead1で回すべきです。
ところで,8で割っているのは何故?
> A[0] = FileData[2*i];
> B[0] = FileData[2*i+1];
> FileData[2*i] = B[0];
> FileData[2*i+1] = A[0];
BYTE temp;
temp = FileData[2 * i];
FileData[2 * i] = FileData[2 * i + 1];
FileData[2 * i + 1] = temp;
> /* ファイル書き込み */
> WriteFile( hFile2, &FileData, BUFFER_SIZE, &dwRead1, NULL );
書き込みサイズはどう考えてもdwRead1だと思いますが……。
あと,書き込み失敗を考えたほうがよいです。
#本当は読み込み失敗も。
tetrapodさん お返事ありがとうございます。
そうですか・・・ バグってましたか・・・(汗)
わざわざどうもありがとうございます。
iostreamの方ですか?
はい!わかりました!
iostreamのところ勉強して
出来上がったらソースを掲載したいと思います。
ありがとうございました。
>メモリの確保・解放を自分で行うことでできます。
>malloc/freeとか,new[]/delete[]とか,HeapAlloc/HeapFreeとか。
>C++使っているならstd::vectorが楽です。
>
>ただ,今回のような場合はファイルマッピングを使って処理した方が簡単だと思います。
はい! ファイルマッピングについてチェックしておりましたが・・・
1BYTEの取り出し方(BYTEずつの移動等)が掲載されているサイトが
見つからず断念しました。。。
>スタックをオーバーしたのでしょう。
>VC++のスタックは1MBなので,20MBの自動変数を確保することはできません。
>
>ファイル全体を一度に読み込もうとせず,
>何回かに分けて読み込むと良いです。
そういう風に書いておりましたが・・・
読み込み→変換→読み込み→変換→読み込み・・・っと
せっかく変換されたデータが消えてしまいます。
そりゃそうですよね・・・ そのまま上書きしてしまうプログラム書いているから・・・
実際、変換した後、どうやってデータを蓄えてどうすればいいのか?
蓄えたデータをまとめて書き込む方法がわかりませんでしたので
1回で読み込むように書いたのですけど・・・(汗)
>INVALID_HANDLE_VALUEをCloseHandleで閉じる必要はないですし,閉じてはいけません。
>INVALID_HANDLE_VALUEはあくまでinvalidなハンドルです。
ご指摘ありがとうございます。修正します。
>せっかくファイルサイズを得たのに,
>BUFFER_SIZEで読み込むのは何故?
>#まぁ,BUFFER_SIZE > dwFileSize1であっても問題はないのだが……。
バッファサイズが超えたら・・・ エラーになるのかと・・・ すいません・・・
>BUFFER_SIZEで回すのではなく,dwRead1で回すべきです。
ご指摘ありがとうございます。修正します。
>ところで,8で割っているのは何故?
i < BUFFER_SIZE で書いて実行したところ・・・
実行中に・・・ 強制終了(画面が消える現象)が起こりました。
下の変換作業で・・・ アドレスエラーが起こったのかと思い、
BUFFER_SIZE - 1 と書いても上記の現象が起こり、
BUFFER_SIZEのサイズってビットで、
取り出しているのがバイトだから・・・
8で割ればいいのかと・・・(滝汗汗汗汗汗汗)
>BYTE temp;
>temp = FileData[2 * i];
>FileData[2 * i] = FileData[2 * i + 1];
>FileData[2 * i + 1] = temp;
YuOさんおっしゃっていたこと
ぜんぜん参考にしておりませんでした・・・(苦笑)
>書き込みサイズはどう考えてもdwRead1だと思いますが……。
>あと,書き込み失敗を考えたほうがよいです。
>#本当は読み込み失敗も。
ご指摘ありがとうございます。修正します。
できてから例外処理を考えております。
まずは動かさないと・・・w
もう少しこのソースでがんばってみます!
はやく【解決】印を付けるように努めます。
tetrapodさん
一応作成しました。
実際テストしところ・・・
自分のプログラムミス?
スタックオーバーフロー??
バイナリデータの途中にEOF???
はっきりとわかりませんが・・・
出力結果が、途中で終っています。
以下がプログラムソースです。
----------------------------------
#include <windows.h>
#include "BigEndian.h"
#include <stdio.h>
#include <stdlib.h> /* exit( ) で必要 */
#include <iostream.h>
DWORD BigEndian(LPCTSTR lpFileName1)
{
FILE *infp;
FILE *outfp;
int c1;
int c2;
if ((infp = fopen("4m", "r")) == NULL) { /* ファイルを開けなければ */
/* メッセージを表示して */
fprintf ( stderr, "Can't Open C Source File!\n" );
exit (2); /* 終了 */
}
if ((outfp = fopen("test.bin", "w")) == NULL) { /* ファイルを開けなければ */
/* メッセージを表示して */
fprintf ( stderr, "Can't Open C Source File!\n" );
exit (2); /* 終了 */
}
do {
c1=getc(infp);
c2=getc(infp);
putc(c2, outfp);
putc(c1, outfp);
} while (c1!=EOF && c2!=EOF);
fclose(infp);
fclose(outfp);
return SUCCESS;
}
追伸:
読み込みファイルサイズ 4MB
書き込みファイルサイズ 702KB
内容もちょろちょろとデータが変化しております。
文字で読み込むの辛いかな?
> はい! ファイルマッピングについてチェックしておりましたが・・・
> 1BYTEの取り出し方(BYTEずつの移動等)が掲載されているサイトが
> 見つからず断念しました。。。
マッピングに成功すれば,MapViewOfFile APIで得られたポインタを,
BYTEの配列のように扱うことができます。
LPBYTE lpMem = MapViewOfFile( ... );
とやったら,lpMem[0], lpMem[1], ……のようにアクセスできます。
> 読み込み→変換→読み込み→変換→読み込み・・・っと
> せっかく変換されたデータが消えてしまいます。
読み込み→変換→書き込み→読み込み→変換→書き込み……
とすれば,変換されたデータは消えませんよ。
> バッファサイズが超えたら・・・ エラーになるのかと・・・ すいません・・・
バッファサイズとファイルサイズを比較して,小さい方をとる,という方法で回避できます。
> >ところで,8で割っているのは何故?
> i < BUFFER_SIZE で書いて実行したところ・・・
> 実行中に・・・ 強制終了(画面が消える現象)が起こりました。
2バイト単位でアクセスしているのだから,2で割る必要はあります。
> #include <iostream.h>
これは不要です。
#使っていないから。
さらに,iostream.hは既に標準ライブラリの地位から滑り落ちているので,
iostream.hは使わないようにするのがよいです。
#確か,.Net 2003ではiostream.h系列は削除されたはず。
標準C++ライブラリのiostream機能を利用する場合は,
#include <iostream>
のようになります。
> if ((infp = fopen("4m", "r")) == NULL) { /* ファイルを開けなければ */
> if ((outfp = fopen("test.bin", "w")) == NULL) { /* ファイルを開けなければ */
バイナリを扱うので,
if ((infp = fopen("4m", "rb")) == NULL) {
や
if ((outfp = fopen("test.bin", "wb")) == NULL) {
のように,バイナリモードで開く必要があります。
> do {
> c1=getc(infp);
> c2=getc(infp);
> putc(c2, outfp);
> putc(c1, outfp);
> } while (c1!=EOF && c2!=EOF);
EOFの判断は書き込む前に行う必要があります。
while ((c1 = getc(infp)) != EOF && (c2 = getc(infp)) != EOF) {
putc(c2, outfp);
putc(c1, outfp);
}
if (c1 != EOF) { /* c2の読み込みでEOFになった場合 */
putc(c1, outfp); /* 1バイトだけ読み出せていた分を書き出す
}
お世話になっております。
KENです。
やっとできました! (T-T)号泣
YuO様 並びに tetrapod様 JUN2様だと思っております。
深くお礼を申し上げます。
まだ、保険が1つできただけでありまして・・・
ファイルマッピングについても検討したいと思います。
もう、少しお付き合い宜しくお願い致します。
>マッピングに成功すれば,MapViewOfFile APIで得られたポインタを,
>BYTEの配列のように扱うことができます。
ありがとうございます。 今から検討に入ります。
今回のプログラムよりもマッピングの方が
パフォーマンスが速いのですかね?
今のプログラムのパフォーマンス(4MB→2秒程度)でも充分満足ですけど・・・
いろいろと検証したいので。
>読み込み→変換→書き込み→読み込み→変換→書き込み……
>とすれば,変換されたデータは消えませんよ。
これで実現できました! (TOT)号泣
読み込み→変換→書き込み→読み込み→変換→書き込みっと
ReadFileを呼び出すごとに先頭から上書きされるじゃないかと
思いながらダメもとでヤってみたら動きました!
ありがとうございます ^^
>バッファサイズとファイルサイズを比較して,小さい方をとる,という方法で回避できます。
はい! 参考になりました!
>2バイト単位でアクセスしているのだから,2で割る必要はあります。
C言語以前の問題ですね・・・ (--;;
すいません・・・。。。
以下にソース掲載します。
--------------------------------------------------------
#include <windows.h>
#include "BigEndian.h"
#define BUFFER_SIZE 100000 //100KB
DWORD BigEndian(LPCTSTR lpFileName1)
{
HANDLE hFile1, hFile2;
DWORD dwRead1;
DWORD dwFileSize;
DWORD dwLoop, dwSurp;
DWORD i,j;
BYTE FileData[BUFFER_SIZE];
BYTE temp;
hFile1 = CreateFile( lpFileName1,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if( hFile1 == INVALID_HANDLE_VALUE ){
return NOT_EXIST_FILE1;
}
hFile2 = CreateFile( "test.bin",
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL );
if( hFile2 == INVALID_HANDLE_VALUE ){
return NOT_EXIST_FILE2;
}
dwFileSize = GetFileSize( hFile1, NULL );
/* ファイルサイズ 商 */
dwLoop = dwFileSize / BUFFER_SIZE;
/* ファイルサイズ 余り */
dwSurp = dwFileSize % BUFFER_SIZE;
/* ファイルサイズ 商 */
for( j = 1; j <= dwLoop; j++ ){
/* ファイル読み込み */
ReadFile( hFile1, &FileData, BUFFER_SIZE, &dwRead1, NULL );
/* ビックエンディアン */
for( i=0; i < dwRead1 / 2; i++ ){
temp = FileData[2 * i];
FileData[2 * i] = FileData[2 * i + 1];
FileData[2 * i + 1] = temp;
}
/* ファイル書き込み */
WriteFile( hFile2, &FileData, dwRead1, &dwRead1, NULL );
}
/* ファイルサイズ 余り */
ReadFile( hFile1, &FileData, dwSurp, &dwRead1, NULL );
for( i=0; i < dwRead1 / 2; i++ ){
temp = FileData[2 * i];
FileData[2 * i] = FileData[2 * i + 1];
FileData[2 * i + 1] = temp;
}
WriteFile( hFile2, &FileData, dwRead1, &dwRead1, NULL );
CloseHandle( hFile1 );
CloseHandle( hFile2 );
return SUCCESS;
}
>これは不要です。
>#使っていないから。
>
>さらに,iostream.hは既に標準ライブラリの地位から滑り落ちているので,
>iostream.hは使わないようにするのがよいです。
>#確か,.Net 2003ではiostream.h系列は削除されたはず。
>
>標準C++ライブラリのiostream機能を利用する場合は,
>#include <iostream>
>のようになります。
#include <iostream> このように書いたのですけど・・・
コンパイルできませんでした・・・。
環境 Visual C++ 6.0 です。
エラー内容は・・・
eh.h is only for C++!
>バイナリを扱うので,
>if ((infp = fopen("4m", "rb")) == NULL) {
>や
>if ((outfp = fopen("test.bin", "wb")) == NULL) {
>のように,バイナリモードで開く必要があります。
ご指摘ありがとうございました。
そこまで深く読んでませんでした・・・ すいません・・・。
>EOFの判断は書き込む前に行う必要があります。
はい! しっかり4MB最後まで読み込みました!
ありがとうございます ^^
以下がソースです。
----------------------------------------------------
#include <windows.h>
#include "BigEndian.h"
#include <stdio.h>
#include <stdlib.h> /* exit( ) で必要 */
#include <iostream.h>
DWORD BigEndian(LPCTSTR lpFileName1)
{
FILE *infp;
FILE *outfp;
int c1;
int c2;
if ((infp = fopen("4m", "rb")) == NULL) { /* ファイルを開けなければ */
/* メッセージを表示して */
fprintf ( stderr, "Can't Open C Source File!\n" );
exit (2); /* 終了 */
}
if ((outfp = fopen("test.bin", "wb")) == NULL) { /* ファイルを開けなければ */
/* メッセージを表示して */
fprintf ( stderr, "Can't Open C Source File!\n" );
exit (2); /* 終了 */
}
while ((c1 = getc(infp)) != EOF && (c2 = getc(infp)) != EOF) {
putc(c2, outfp);
putc(c1, outfp);
}
if (c1 != EOF) { /* c2の読み込みでEOFになった場合 */
putc(c1, outfp); /* 1バイトだけ読み出せていた分を書き出す */
}
fclose(infp);
fclose(outfp);
return SUCCESS;
}
> #include <iostream> このように書いたのですけど・・・
> コンパイルできませんでした・・・。
> 環境 Visual C++ 6.0 です。
> エラー内容は・・・
> eh.h is only for C++!
えっ……?C++としてコンパイルしていたのでは無かったのですか?
iostream.hって元々C++用のヘッダファイルですが……。
って……待てMS!
in iostream.h
>#ifdef __cplusplus
(snip)
>#endif /* __cplusplus */
Cでコンパイルしてもエラーの一つも表示せずかい……。
というわけで,
#include <iostream.h>
をCのプログラムに書くのはやめた方がよいです。
#それがあるとC++のプログラムと見なされます。
お世話になっております。
KENです。
>#include <iostream.h>
>をCのプログラムに書くのはやめた方がよいです。
すいません・・・。 コンパイルエラーが起こっていて・・・。
何か足りないのでは?っとファイル入出力にiostreamの
ライブラリ?が必要かと思い付け加えておりました。
はい。#include <iostream.h>消してコンパイル通りました。
ファイルマッピングができました。
今回、ファイルマッピング使って処理をおこなうことに決めました。
これで問題無いと思うので解決させて頂きます。
YuOさん
最後までご親切にご指導して頂き誠にありがとうございました。(号泣)
本当に本当に感謝しております。
ソースを掲載します。
--------------------------------------
#include <windows.h>
#include "BigEndian.h"
DWORD BigEndian(LPCTSTR lpFileName1)
{
HANDLE hFile1, hMap;
DWORD dwFileSize;
DWORD i;
LPBYTE FileData;
BYTE temp;
hFile1 = CreateFile( lpFileName1,
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if( hFile1 == INVALID_HANDLE_VALUE ){
return NOT_EXIST_FILE1;
}
dwFileSize = GetFileSize( hFile1, NULL );
if(hFile1 != INVALID_HANDLE_VALUE){
hMap = CreateFileMapping(hFile1, NULL, PAGE_READWRITE, 0, dwFileSize, NULL);
if(hMap != INVALID_HANDLE_VALUE){
FileData = (LPBYTE)MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, dwFileSize);
}
}
if((hFile1 == INVALID_HANDLE_VALUE) || (hMap == INVALID_HANDLE_VALUE) || !FileData){
return NOT_EXIST_FILE1;
}
/* ビックエンディアン */
for( i=0; i < dwFileSize / 2; i++ ){
temp = FileData[2 * i];
FileData[2 * i] = FileData[2 * i + 1];
FileData[2 * i + 1] = temp;
}
if(FileData){
FlushViewOfFile(FileData, dwFileSize);
UnmapViewOfFile(FileData);
}
if(hMap != INVALID_HANDLE_VALUE) CloseHandle(hMap);
if(hFile1 != INVALID_HANDLE_VALUE) CloseHandle(hFile1);
return SUCCESS;
}