ファイルの重複チェック
ファイルの中身を逆にする
csvファイルを愛でる
ファイルを分割
ファイルを結合
連長圧縮でもしてみる
6chwavを2chにダウンミックスしてみる
Thumbs.dbからjpegファイルを抽出する
ファイルの重複チェックです。一応DOS窓にはFCコマンドがありますが。
コードです。ファイルサイズを比較して、同じなら内容を比較します。それだけです。
#include <stdio.h> #include <stdlib.h> #include <string.h> /* f1とf2を比較。-1,-2ファイル開けない0=内容違う1=同じ */ int fc(char *f1, char *f2) { FILE *fp[2]; char *buf[2]; long siz[2]; int i, ret = 0; if ((fp[0] = fopen(f1, "rb")) == NULL) return -1; if ((fp[1] = fopen(f2, "rb")) == NULL) { fclose(fp[0]); return -2; } /* ファイルサイズ調べる */ for (i = 0; i < 2; i++){ fseek(fp[i], 0, SEEK_END); siz[i] = ftell(fp[i]); rewind(fp[i]); } if (siz[0] != siz[1]) goto owari; /* ファイルの内容を比較する */ for (i = 0; i < 2; i++) { buf[i] = malloc(siz[0]); fread(buf[i], 1, siz[0], fp[i]); } if (!memcmp(buf[0], buf[1], siz[0])) ret = 1; for (i=0; i < 2; i++) free(buf[i]); owari: for (i=0; i < 2; i++) fclose(fp[i]); return ret; } /* エラーメッセージ出して終わり */ void errmsg(char *mes) { printf("エラーです。%s\n", mes); exit(0); } main(int argc, char **argv) { int r; if (argc < 3) errmsg("引数不足"); if ((r = fc(argv[1], argv[2])) < 0) errmsg("ファイル開けない"); printf("[%s]と[%s]は%s\n", argv[1], argv[2], r ? "同じ" : "違う" ); return 0; }
何の意味があるのか分かりませんが。ファイルを隠すとき位しか用途はなさそうですが。 大きなファイルの場合、時間がかかると思います。
コードです。"ファイルをいじる"というよりは"ポインタをいじる"ような感じですけど。
#include <stdio.h> #include <stdlib.h> #include <string.h> /* ファイルをリバースして上書き 1=成功 0=失敗 */ int fr(char *filename) { FILE *fp; long siz; char *buf, *atama, *oshiri; char tmp; fp = fopen(filename, "r+b"); if(fp == NULL) return 0; /* bufにファイル入れる */ fseek(fp, 0, SEEK_END); siz = ftell(fp); rewind(fp); atama = buf = malloc(siz); oshiri = buf + siz -1; fread(buf, 1, siz, fp); rewind(fp); /* リバース */ do { tmp = *atama; *atama = *oshiri; *oshiri = tmp; } while (++atama < --oshiri); /* 書き込んで終わり */ fwrite(buf, 1, siz, fp); free(buf); fclose(fp); return 1; } int main(int argc , char **argv) { if (argc > 1) while (--argc > 0) printf("[%s]リバースは%s\n", argv[argc], fr(argv[argc]) ? "成功" : "失敗"); return 0; }
最近話題のcsvファイルを読み込んで、htmlのtableに変換する画期的なプログラムです。 ファイルをfgetsで1行ずつ読み込んでstrtokで区切っています。 でもstrtokの仕様で「,,」のような空のデータは無視されます。
/** * csv -> table sky_tblerer * 注意:空(,,,こんなの)は無視されるので注意 ******************************/ #include <stdio.h> #include <string.h> #define err(x) {printf("%s\n", (x));return;} main(int argc, char* argv[]) { FILE *fp; char buf[200]; char *p; if (argc < 2) err("引数不足。csvファイルを指定してくださいね♪"); if ((fp = fopen(argv[1], "r") == NULL) err("ファイルを読み込めないんですが"); printf("<table border=1>\n"); /* 1行ずつ読み込む */ while (fgets(buf, 200, fp) != NULL) { /* コンマ、改行で分割 */ p = strtok(buf, ",\n"); printf("<tr>"); while (p != NULL) { printf("<td>%s</td>", p); p = strtok(NULL, ",\n"); } printf("</tr>\n");/* ここで改行 */ } printf("</table>"); fclose(fp); return ; }
今回はファイル分割です。結合バッチファイルも作ってしまいます。だから、DOS窓専用です。
EXTは分割ファイルの拡張子です。.kumaにしています。 usage関数はソースファイル名を好きな使えるように__FILE__マクロを使っています。 でもやめたほうがいいかもしれません。
#include <stdio.h> #include <stdlib.h> #include <string.h> #define EXT ".kuma" #define BATFILE "ketugou.bat" /* 引数足りんときの終了 */ void usage(void) { char file[100]; char *p; /* ファイル名を作る */ strcpy(file, __FILE__); p = strrchr(file, '.'); if (p != NULL) *p = 0; printf("ファイル分割ソフトです。\n" "%s 分割するファイル 分割サイズ\n", file); exit(0); } /* エラー終了 */ void era_desu(char *kome) { printf("エラーです。%s", kome); exit(1); } /* 結合バッチを作る */ void Ketugou(char *fname, int kaunto) { FILE *fp; int c; fp = fopen(BATFILE, "w"); if (fp == NULL) era_desu("結合バッチ作れん"); fprintf(fp, "copy /b "); for (c = 0; c < kaunto-1; c++) fprintf(fp ,"\"%s_%d%s\" + ",fname, c, EXT); fprintf(fp ,"\"%s_%d%s\" \"%s\"\n",fname, kaunto-1, EXT, fname); fprintf(fp ,"del *%s %s\n", EXT, BATFILE);/* 分割ファイルと自分自身を削除 */ fclose(fp); puts("結合バッチを作りました。"); } /* メーン関数 */ main(int argc, char **argv) { char fname[100]; char outfile[100]; unsigned size; char *buf; FILE *ifp, *ofp; int kaunto = 0; size_t read; if (argc < 3) usage();/* 引数不足*/ strcpy(fname, argv[1]); ifp = fopen(fname, "rb"); if (ifp == NULL) era_desu("ファイル開けん。"); size = atoi(argv[2]); if (size < 1) era_desu("分割サイズが変です。"); buf = (char *)malloc(size); if (buf == NULL) era_desu("メモリ無いよ。"); printf("[%s]を[%d]バイトで分割します。\n", fname, size);/* 情報開示 */ /* 入力がなくなるまで頑張る */ while ((read = fread(buf, 1, size, ifp))) { sprintf(outfile, "%s_%d%s",fname, kaunto, EXT);/* 出力ファイル名生成 */ kaunto++; /* 書き込む */ ofp = fopen(outfile, "wb"); if (ofp == NULL) era_desu("出力ファイル開けん。"); if (fwrite(buf, 1, read, ofp) == 0) era_desu("書き込みエラー。");; fclose(ofp); printf("[%s]を作りました。(%dバイト)\n", outfile, read);//情報開示 } fclose(ifp); Ketugou(fname, kaunto);//結合バッチファイルを作る。 puts("終わり"); }
ファイルの分割が出来たらファイルの結合もしたいのが人情。 ということで結合ソフトを作りました。
使い方は引数に、結合後ファイル名 結合するファイル........ を入れます。
#include <stdio.h> #define bufsiz 4096 /* 引数不足 */ void usage(void) { puts("ファイル結合ソフトです。\n" "引数について\n" "1個目:結合後ファイル名\n" "2個目から結合するファイル(順番に注意)"); exit(0); } main(int argc, char *argv[]) { FILE *infp, *outfp; char buf[bufsiz]; int p; int readsize; if (argc < 3) usage(); outfp = fopen(argv[1], "wb"); if (outfp == NULL) { puts("出力ファイル開けん。終わり"); return -1; } printf("ファイル[%s]結合開始!\n", argv[1]); for (p=2; p < argc; p++) { infp = fopen(argv[p], "rb"); if (outfp == NULL) { puts("入力ファイル開けん。終わり"); return -2; } printf("%s →%s\n", argv[p], argv[1]); while ((readsize = fread(buf, 1, bufsiz, infp)) != 0) fwrite(buf, 1, readsize, outfp); fclose(infp); } fclose(outfp); return ; }
さて、ろくなプログラムを作れない私ですが、無謀にも連長圧縮に挑戦してみました。
ランレングス圧縮とも言うそうです。AAABBCCCCDEをA3B2C4D1E1というようにするらしいです。 自分自身のコードを連長圧縮したところ、160%になってしまいました。 main関数のエンコードとデコードの分岐がムダなような気もします。
使い方は[入力ファイル 出力ファイル]でエンコード、 [入力ファイル 出力ファイル -d]でデコードです。
/*連長圧縮 ver0.2 */ #include <stdio.h> #include <stdlib.h> #include <string.h> /* バッファサイズ。絶対に偶数。 */ #define PSIZ 4096 #define ENCODE 0 #define DECODE 1 typedef struct { unsigned char *sbuf; /* 元のデータ */ int ssiz; /*sbufのサイズ */ unsigned char *dbuf; /*圧縮データ */ int dsiz; /*dbufのサイズ */ } RLS1; /* エンコード関数 */ int rlenc1(RLS1 *rl) { unsigned char cnt, c; int n, m; cnt = n = m = 0; while (n < rl->ssiz) { c = rl->sbuf[n]; cnt = 0; while (c == rl->sbuf[n]) { cnt++; n++; if ((n >= rl->ssiz) || (cnt >= 255)) break; } rl->dbuf[m] = c; rl->dbuf[m+1] = cnt; m +=2; } rl->dsiz = m; return rl->dsiz; } /* デコード関数 */ int rldnc1(RLS1 *rl) { int n; rl->dsiz = 0; for (n=0; n < rl->ssiz ;n+=2 ) { memset(&rl->dbuf[rl->dsiz], rl->sbuf[n], rl->sbuf[n+1]); rl->dsiz += rl->sbuf[n+1]; } return rl->dsiz; } /* RLS1構造体前処理 (メモリ確保するだけ) */ int rl1_syokika(RLS1 *rl, int siz, int mode) { rl->sbuf = malloc(siz); if (rl->sbuf == NULL) return 0; if (mode == ENCODE) { rl->dbuf = malloc(siz*2);/* 最大で2倍になる */ } else { rl->dbuf = malloc(siz*255);/* 最大で255倍になる */ } if (rl->dbuf == NULL) return 0; return 1; } /* RLS1構造体後処理(メモリ開放するだけ) */ int rl1_owari(RLS1 *rl) { free(rl->sbuf); free(rl->dbuf); return 1; } int main(int argc, char **argv) { FILE *ifp, *ofp; RLS1 rl; int sall, dall; if (argc < 3) { puts("引数の与え方は 入力ファイル 出力ファイル [-d] です。\n"); return 1; } ifp = fopen(argv[1], "rb"); if (ifp == NULL) { puts("エラー!:入力ファイル開けん!"); return 2; } ofp = fopen(argv[2], "wb"); if (ofp == NULL) { puts("エラー!:出力ファイル開けん!"); fclose(ifp); return 3; } sall = dall = 0;/* 初期化 */ if (argc >= 4 && !memcmp(argv[3], "-d", 2)) { /* デコード */ rl1_syokika(&rl, PSIZ, DECODE); printf("復元開始\n%s -> %s\n", argv[1], argv[2]); while ((rl.ssiz = fread(rl.sbuf, 1, PSIZ, ifp)) > 0) { rldnc1(&rl); fwrite(rl.dbuf, 1, rl.dsiz, ofp); printf("%d -> %d (%d%%)\n", rl.ssiz, rl.dsiz , rl.dsiz * 100 / rl.ssiz); sall += rl.ssiz; dall += rl.dsiz; } printf("-------------------------------------------\n" "%d -> %d (%d%%)\n", sall, dall , dall * 100 / sall); puts("復元完了!"); } else { /* エンコード */ rl1_syokika(&rl, PSIZ, ENCODE); printf("圧縮開始\n%s -> %s\n", argv[1], argv[2]); while ((rl.ssiz = fread(rl.sbuf, 1, PSIZ, ifp)) > 0) { rlenc1(&rl); fwrite(rl.dbuf, 1, rl.dsiz, ofp); printf("%d -> %d (%d%%)\n", rl.ssiz, rl.dsiz , rl.dsiz * 100 / rl.ssiz); sall += rl.ssiz; dall += rl.dsiz; } printf("-------------------------------------------\n" "%d -> %d (%d%%)\n", sall, dall , dall * 100 / sall); puts("圧縮完了!"); } /* 後始末 */ rl1_owari(&rl); fclose(ifp); fclose(ofp); return 0; }
AC3、DTSなど5.1chサラウンドがもてはやされた時代がありましたが、昨今ではあまり人気がありませんよね。
ここではすでに5.1ch(6ch)のwavファイルにデコードされたものを2chにダウンミックスする方法を考えます。
6chの構造はFL,FR,C,LFE,SL,SRの順に並んでいるそうです。 それ以外は普通のwavファイルと同じらしい。
次のプログラムは6chwavを2chに変換します。 引数は1つめが入力ファイル名、2つめが出力ファイル名(省略可)です。
ファイルサイズは出力サイズが4GBつまり12GBの入力までなら多分大丈夫だとおもいます。
//6ch->2ch ダウンミックス #include <windows.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define SAMPLECOUNT 48000 #define DEFAULT_OUTFILENAME "鈴木えみちゃんは天使.wav" #define MEMSIZE (SAMPLECOUNT * sizeof(short) * 12) //6ch配置対応表 enum { FL = 0, FR, C, LFE, SL, SR, }; enum { DM_NOERROR = 0, DM_ARG_ERROR, DM_INIT_ERROR, DM_WAVHDR_ERROR, DM_END, }; #pragma pack(push,1) //wavファイルのヘッダ typedef struct{ char RIFF[4];//"RIFF" DWORD bytes;//(ファイルのバイト数)-8 char WAVE[4];// "WAVE" char fmt[4];// "fmt " DWORD siz_wf;//PCMWAVEFORMAT構造体のバイト数=常に16 //PCMWAVEFORMAT構造 WORD wFormatTag; //普通は1 WORD nChannels; //チャンネル数 DWORD nSamplesPerSec; //サンプリングレート DWORD nAvgBytesPerSec; //1秒間あたりのバイト数 WORD nBlockAlign; //1サンプルに必要なバイト数 ch * bits/8 WORD wBitsPerSample;//量子化ビット数 char data[4];//"data" DWORD pcmbytes;//波形データのバイト数 } WAVHEADER; #pragma pack(pop) //ダウンミックスに使う変数 typedef struct { int smpl;//サンプル数 short *in;//入力バッファ short *out;//出力バッファ FILE *ifp;//入力ファイル FILE *ofp;//出力ファイル WAVHEADER wh;//WAVHEADER構造体 } DownMixArg; //プロトタイプ //公開可能 int init_downmix(DownMixArg *pdma, char *infile, char *outfile, int smpl); int downmix62(DownMixArg *pdma); int end_downmix(DownMixArg *pdma); //下請け void free_DownMixArg(DownMixArg *pdma); short clip(int i); void err_exit(char *mes, int code); void defaultname(char *buf); //初期化 //入力ファイル名infile、出力ファイル名outfile、サンプル数smplを //使って、pdmaを初期化する。 int init_downmix(DownMixArg *pdma, char *infile, char *outfile, int smpl) { char outfile2[333]; if (pdma == NULL || infile == NULL || smpl == 0) return DM_ARG_ERROR;//引数がおかしい if (outfile == NULL) { defaultname(outfile2); outfile = outfile2; } pdma->in = pdma->out = NULL; pdma->ifp = pdma->ofp = NULL; //初期化 pdma->ifp = fopen(infile, "rb"); pdma->ofp = fopen(outfile, "wb"); pdma->smpl = smpl; pdma->in = malloc(sizeof(short) * 6 * pdma->smpl); pdma->out = malloc(sizeof(short) * 2 * pdma->smpl); if (pdma->ifp == NULL || pdma->ofp == NULL || pdma->in == NULL || pdma->out == NULL) { free_DownMixArg(pdma); return DM_INIT_ERROR; } //続いて初期設定 fread(&pdma->wh, sizeof(WAVHEADER), 1, pdma->ifp);//ヘッダ読み込み if (pdma->wh.nChannels != 6 || pdma->wh.wBitsPerSample != 16 || memcmp(pdma->wh.data, "data", 4) != 0) { free_DownMixArg(pdma); return DM_WAVHDR_ERROR; } pdma->wh.wFormatTag = WAVE_FORMAT_PCM;//=1 pdma->wh.nChannels = 2;//2ch pdma->wh.nBlockAlign = pdma->wh.nChannels * pdma->wh.wBitsPerSample/8; pdma->wh.nAvgBytesPerSec = pdma->wh.nSamplesPerSec * pdma->wh.nBlockAlign; //とりあえず仮のサイズにしてヘッダを書き込む pdma->wh.pcmbytes= pdma->wh.nAvgBytesPerSec * 300;//300秒 pdma->wh.bytes = pdma->wh.pcmbytes + sizeof(WAVHEADER) - 8; fwrite(&pdma->wh, sizeof(WAVHEADER), 1, pdma->ofp); pdma->wh.pcmbytes = 0;// return DM_NOERROR; } //DownMixArg構造体を使ってダウンミックスを行う //DM_NOERRORが戻れば繰り返す。それ以外はend_downmixを呼ぶ。 int downmix62(DownMixArg *pdma) { int ret, j; short *pout = pdma->out; short *pin = pdma->in; if (pdma == NULL) return DM_ARG_ERROR;//引数がおかしい if (pdma->ifp == NULL || pdma->ofp == NULL || pdma->in == NULL || pdma->out == NULL) { return DM_ARG_ERROR;//引数がおかしい } //読み込む ret = fread(pdma->in, 2, 6 * pdma->smpl, pdma->ifp);// if (ret < 6) return DM_END;//読み込むものがない。 //ダウンミックス j = 0; while (ret >= 6) { *pout++ = clip(pin[FL] + pin[C] + pin[SL]); *pout++ = clip(pin[FR] + pin[C] + pin[SR]); j += 2; ret -= 6; pin += 6; } //書き込む ret = fwrite(pdma->out, sizeof(short), j, pdma->ofp); pdma->wh.pcmbytes += (ret * sizeof(short)); return DM_NOERROR; } //終了およびメモリ開放 int end_downmix(DownMixArg *pdma) { if (pdma == NULL) return DM_ARG_ERROR;//引数がおかしい if (pdma->ifp == NULL || pdma->ofp == NULL) { free_DownMixArg(pdma); return DM_ARG_ERROR;//引数がおかしい } //正しいサイズを書き込む fseek(pdma->ofp, 0, SEEK_SET); pdma->wh.bytes = pdma->wh.pcmbytes + sizeof(WAVHEADER) - 8; fwrite(&pdma->wh, sizeof(WAVHEADER), 1, pdma->ofp); free_DownMixArg(pdma);//開放 return DM_NOERROR; } //DownMixArg構造体を開放 void free_DownMixArg(DownMixArg *pdma) { if (pdma->in) free(pdma->in); if (pdma->out) free(pdma->out); if (pdma->ifp) fclose(pdma->ifp); if (pdma->ofp) fclose(pdma->ofp); pdma->in = pdma->out = NULL; pdma->ifp = pdma->ofp = NULL; } //クリップを防ぐ short clip(int i) { if (i > 32767) { return 32767; } else if (i < -32768) { return -32768; } return i; } //mesを表示して終了 void err_exit(char *mes, int code) { puts(mes); exit(code); } //"アプリのディレクトリ+DEFAULT_OUTFILENAME"をbufに返す void defaultname(char *buf) { char modnam[333], drv[333], dir[333]; GetModuleFileName(GetModuleHandle(NULL), modnam, 333); _splitpath(modnam, drv, dir, NULL, NULL); sprintf(buf, "%s%s%s", drv, dir, DEFAULT_OUTFILENAME); } int main(int argc, char *argv[]) { DownMixArg dma; if (argc < 2) err_exit("引数?", 1); if (init_downmix(&dma, argv[1], argv[2], SAMPLECOUNT) != DM_NOERROR) { err_exit("初期化失敗", 2); } while (downmix62(&dma) == DM_NOERROR) { printf("out %8.3f MB ( %5d sec)\r", dma.wh.pcmbytes/(1024.*1024), dma.wh.pcmbytes/dma.wh.nAvgBytesPerSec); } end_downmix(&dma); puts("\nおわり"); return 0; }
難しいことを考えずにjpgのマジックナンバー(0xFF, 0xD8)を検出してファイルに書き出すだけです。
#include <stdio.h> int main(int argc, char **argv) { FILE *ifp, *ofp=NULL; int c = 0 , d = 0, n = 0; char out[300]; if ((ifp = fopen(argv[1], "rb")) == NULL) return 1; while ((c = fgetc(ifp)) != EOF) { if (d == 0xFF && c == 0xD8) {//jpegのマジックナンバー //前回のファイルを閉じる if (ofp!=NULL) { printf("抽出 : %s[%dbytes]\n", out,ftell(ofp)); fclose(ofp); } //新たにファイルを作る sprintf(out, "%s-%04d.jpg", argv[1], n++); ofp = fopen(out, "wb"); if (ofp!=NULL) fputc(d, ofp); } if (ofp!=NULL) fputc(c, ofp); d = c;// } if (ofp!=NULL) fclose(ofp); fclose(ifp); return 0; }