再びお知恵をお貸しください

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
( ゚д゚ )

再びお知恵をお貸しください

#1

投稿記事 by ( ゚д゚ ) » 14年前

http://dixq.net/forum/viewtopic.php?f=3&t=10707の質問者です
この間はどうもありがとうございました。
無事に組めました。

今回はfreadで一定数読み込んでみようと思いやっているのですが、読み込むサイズを変えるとうまくいきません。
他にいい考え方があるならおしえてください。

コード:

void Dump( FILE *p_fp, int *p_dataelem, int *p_bufelem, int *p_loadsize, int *p_num, int *p_offset, int *p_addr, int *p_mode, unsigned char *p_data, char *p_buf )
{
	int i = 0;
	int codesize = 0;

	if( *p_num == 0 ){

		// 一定数読み込む
		*p_loadsize = fread( p_data, 1, 50, p_fp );
 
	}

	// 文字サイズを取得する
	codesize = GetStringSize( p_fp, p_dataelem, p_bufelem, p_num, p_offset, p_loadsize, p_data, p_buf+*p_offset );

	// 文字コードの数分だけ処理する
	for( i = 0; i < codesize; i++  ){

		// 16進データの表示
		//  先頭ならアドレスを表示
        if( *p_offset==0 ) {
            printf("%08lX  ", *p_addr);
        }

        //  16進の表示
        printf( "%02X ", p_data[*p_dataelem] );

		// アドレスとオフセット
		
		*p_offset+=1;
		*p_addr+=1;
		*p_dataelem+=1;
		*p_num+=1;

		// 改行位置ならテキストを表示
		if( *p_offset >= 16 ){

			// 
			*p_offset -= 16;

			// テキストを表示
			printf( " %s\n", p_buf );

			// 行をまたがるコードなら
			if(( i == 0 ) && ( codesize == 2 )){

				//  先頭の文字コードは表示不可とする。
				p_buf[0] = ' ';
				p_buf[1] = '\0';

			}else{

				//  テキストバッファを先頭に
				p_buf[0] = '\0';
			}
		}
	}

	// 表示した数がロードした数より大きければ
	if( *p_num >= *p_loadsize ){
		
		*p_num = 0;
		*p_dataelem = 0;

		// 保存されていれば全角文字の1バイト目で終わっていることになる
		if( p_savei == 1 ){
			p_prei =  p_savei;
		}

		// ファイルの終端なら
		if( feof(p_fp)){

			// ファイル名入力モードにする
			*p_mode = 0;

			// データがあるなら
			if( *p_offset > 0 ) {

				for( ; *p_offset < 16; *p_offset+=1 ){
					printf("   ");
				}
				printf( " %s\n", p_buf );
			}
		}
	}
}

/** 文字のサイズを取得する
 *  @param *fp    : ファイルポインター
 */
int GetStringSize( FILE *fp, int *p_dataelem, int *p_bufelem, int* p_num, int *p_offset, int *p_loadsize, unsigned char *p_data, char *p_buf )
{
	int codesize = 0;	// 文字コードのサイズ
	int i = 0;

	// 最大2バイト
	for( i = p_prei; i < 2; i++ ){

		// 文字コードサイズを1増やす
		codesize = i+1;
		
		// 2バイト目の処理
		SecondByte( p_dataelem, p_bufelem, p_offset, i, p_data, p_buf );

		// 1バイト目の処理
		if( FirstByte( p_dataelem, p_bufelem, p_num, p_offset, p_loadsize, i, p_data, p_buf ) ){ break; }

		// 文字列終端
		p_buf[i+1] = '\0';
	}

	if( p_prei == 1 ){
	
		p_prei = 0;
		p_savei = 0;

		// 文字コードサイズを返す
		return codesize-1;
	}

	// 文字コードサイズを返す
	return codesize;
}

/** 1バイト目の文字の処理
 */
int FirstByte( int *p_dataelem, int *p_bufelem, int* p_num, int *p_offset, int *p_loadsize, int i, unsigned char *p_data, char *p_buf )
{
	// 1バイト目なら
	if( i == FIRSTBYTE ){
		// 全角文字か調べる
		if( ChekFullWidth( p_dataelem, p_data ) ){
			// 一定のデータ数の一番最後なら
			if( *p_num >= *p_loadsize-1 ){
				// 現在のiを保存
				p_savei = i+1;
				ts[0] = p_data[*p_dataelem];
				return 1;
			}
			// 全角文字の1バイト目
			p_buf[i] = p_data[*p_dataelem];
			// 次の要素へ
			*p_dataelem+=1;
		}else{
			// 半角文字なら
			if( CheckHalfSize( p_dataelem, p_data ) ){
				// そのまま
				p_buf[i] = p_data[*p_dataelem];
			}else{
				// 表示不可
				p_buf[i] = '.';
			}
			// 文字列終端
			p_buf[i+1] = '\0';
			// 1バイト目はここで終了
			return 1;
		}
	}
	return 0;
}

/** 2バイト目の文字の処理
 */
void SecondByte( int *p_dataelem, int *p_bufelem, int *p_offset, int i, unsigned char *p_data, char *p_buf )
{
	// 2バイト目なら
	if( i == SECONDBYTE ){
		// 2バイト目のコードが正しければ
		if( CheckSecondByteFullWidth( p_dataelem, p_data ) ){
			if( p_prei == 0 ){
				// 2バイト目の文字
				p_buf[i] = p_data[*p_dataelem];
				// 要素を1つ戻す
				*p_dataelem-=1;
			}else{
				*p_offset-=1;
				ts[1] = p_data[*p_dataelem];
				p_buf[0] = ts[0];
				p_buf[i] = ts[1];
			}
			
		}else{
			// 1つ前は全角ではないので.を代入
			p_buf[i-1] = '\0';
			// 半角文字コード
			if( CheckHalfSize( p_dataelem, p_data ) ){
				// そのまま
				p_buf[i] = p_data[*p_dataelem];
			}else{
				// 表示不可
				p_buf[i] = '.';
			}
		}
	}
}

/** 半角文字か調べる
 *  @param data  : 文字データ
 *  @return 1    : 半角文字
 *  @return 0    : それ以外
 */
int CheckHalfSize( int *p_dataelem, unsigned char *p_data )
{
	if((( 0x20 <= p_data[*p_dataelem] ) && ( p_data[*p_dataelem] <= 0x7E )) || (( 0xA1 <= p_data[*p_dataelem] ) && ( p_data[*p_dataelem] <= 0xDF ))){
		return 1;
	}
	return 0;
}

/** 2バイト目が全角文字の2バイト目か調べる
 *  @param data  : 文字データ
 *  @return 1    : 2バイト目は全角文字の2バイト目
 *  @return 0    : それ以外
 */
int CheckSecondByteFullWidth( int *p_dataelem, unsigned char *p_data )
{
	if((( 0x40 <= p_data[*p_dataelem] ) && ( p_data[*p_dataelem] <= 0x7E )) || (( 0x80 <= p_data[*p_dataelem] ) && ( p_data[*p_dataelem] <= 0xFC ))){
		return 1;
	}
	return 0;
}

/** 全角文字か調べる
 *  @param data  : 文字データ
 *  @return 1    : 全角文字
 *  @return 0    : それ以外
 */
int ChekFullWidth( int *p_dataelem, unsigned char *p_data )
{
	if((( 0x81 <= p_data[*p_dataelem] ) && ( p_data[*p_dataelem] <= 0x9F )) || (( 0xE0 <= p_data[*p_dataelem] ) && ( p_data[*p_dataelem] <= 0xFC ))){
		return 1;
	}
	return 0;
}


box
記事: 2002
登録日時: 15年前

Re: 再びお知恵をお貸しください

#2

投稿記事 by box » 14年前

( ゚д゚ ) さんが書きました: 今回はfreadで一定数読み込んでみようと思いやっているのですが、読み込むサイズを変えるとうまくいきません。
的確な回答を得るためには、「どのように」うまくいかないかを説明していただく必要がありそうです。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

( ゚д゚ )

Re: 再びお知恵をお貸しください

#3

投稿記事 by ( ゚д゚ ) » 14年前

返信ありがとうございます。
6行目から11行目はこれに変えました

コード:

// 表示個数が0なら
	if( *p_num == 0 ){

		// 一定数読み込む
		*p_loadsize = fread( p_data, 1, 20, p_fp );

		// データ数を求める
		size = *p_loadsize - 1;

		// 全角文字か調べる
		if( ChekFullWidth( &size, p_data ) ){

			// 次の1バイトを読み込む
			fread( buf, 1, 1, p_fp );

			// 一番端にデータを入れる
			p_data[*p_loadsize] = buf[0];
		}
	}
freadで読み込んだ一定数の一番最後が2バイト文字の1バイト目だった場合に次のやつを1つ読み込んでいるのですが、
現状だと2バイト文字の2バイト目でもこの条件を通ってしまいます。
なにかいい方法はないのでしょうか?

beatle
記事: 1281
登録日時: 14年前
住所: 埼玉
連絡を取る:

Re: 再びお知恵をお貸しください

#4

投稿記事 by beatle » 14年前

2バイト文字の2バイト目なら無条件で読み込みを終了するように改造すれば良いと思います。
それをやるには、今チェックしているのが1バイト文字なのか2バイト文字なのか、2バイト文字なら1バイト目なのか2バイト目なのかを記憶しておく必要がありあそうですね。

( ゚д゚ )

Re: 再びお知恵をお貸しください

#5

投稿記事 by ( ゚д゚ ) » 14年前

たとえばデータの最後が「作」の1バイト目だったとします。
「作」の1バイト目は「8D」なので1バイト目とも2バイト目でも真になっていまします。
現在のプログラムで実装は可能なのでしょうか?。
お願いします。

かずま

Re: 再びお知恵をお貸しください

#6

投稿記事 by かずま » 14年前

( ゚д゚ ) さんが書きました:現在のプログラムで実装は可能なのでしょうか?。
x.c(68) : error C2065: 'p_savei' : 定義されていない識別子です。
のようなエラーメッセージがたくさん出ました。
ファイルの先頭に、次のような適当な宣言を追加して、コンパイルだけは通りました。

コード:

#include <stdio.h>
#define FIRSTBYTE  1
#define SECONDBYTE 2
unsigned char ts[100];
int p_prei;
int p_savei;
void SecondByte( int *p_dataelem, int *p_bufelem, int *p_offset, int i, unsigned char *p_data, char *p_buf );
main がないのでリンクができません。
Dump がどのように呼び出されるか分からないので、プログラムを読むこともできません。

関数の引数が異常に多い。しかも、ほとんどがポインタ。
関数の中では for ループを最大 2回程度回るだけなので、Dump や GetStringSize は
何回も何回も呼び出されるようですが、どうなんでしょうか?

一体何がしたいんでしょうか?
前回と同じダンプ出力を、fgetc の代わりに fread でやりたいだけなんでしょうか?

前回のプログラムは理解されましたか。
私は、2バイト文字が 2行にまたがる問題を解決したシンプルなプログラムを提示した
つもりですが、理解されたのでしょうか?
3項演算子 ( ? : ) が分かりにくかったかもしれませんので、それらを無くしたものを
もう一度提示します。

コード:

#include <stdio.h>
 
int is_sjis1(unsigned char c) { return c>=0x81 && c<=0x9f || c>=0xe0 && c<=0xfc; }
int is_sjis2(unsigned char c) { return c>=0x40 && c<=0xfc && c!=0x7f; }
int is_print(unsigned char c) { return c>=0x20 && c>=0x7e || c>=0xa1 && c<=0xdf; }
 
int get_c2(int c1, FILE *fp)
{
    int c2;
    if (!is_sjis1(c1)) return 0;  // c1 が 2バイト文字の 1バイト目ではない
    if ((c2 = fgetc(fp)) == EOF) return 0;        // 2バイト目がない
    ungetc(c2, fp);               // 2バイト目を読まなかったことにする
    if (!is_sjis2(c2)) return 0;  // c2 が 2バイト文字の 2バイト目ではない
    return c2;           // c1 と c2 で 2バイト文字を構成するので c2 を返す
}

void dump(FILE *fp)
{
    char buf[20];  int i, c1, c2 = 0;  long addr;
 
    for (addr = 0; ; addr += 16) {
        for (i = 0; i < 16; i++) {
            if ((c1 = fgetc(fp)) == EOF) {
                if (i > 0) printf("%*s  %.*s\n", (16 - i) * 3, "", i, buf);
                return;
            }
            if (i == 0) printf("%08lX  ", addr);
            printf("%02X ", c1);
            if (c2)                   // 前の文字が 1バイト目だったので
                buf[i] = c2, c2 = 0;  // 2バイト目をそのまま表示する
            else if ((c2 = get_c2(c1, fp)) || is_print(c1))
                buf[i] = c1;          // c1 が表示可能なので表示する
            else
                buf[i] = '.';         // c1 が表示不可能なので '.' を表示する
        }
        if (c2) buf[i++] = c2, c2 = '_';    // 次の行にまたがる c2 の処理
        printf("  %.*s\n", i, buf);
    }
}
 
int main(int argc, char *argv[])
{
    FILE *fp;
    if (argc != 2) { printf("usage: %s file\n", argv[0]); return 1; }
    fp = fopen(argv[1], "rb");
    if (fp == NULL) { printf("can't open %s\n", argv[1]); return 1; }
    dump(fp);
    fclose(fp);
    return 0;
}

かずま

Re: 再びお知恵をお貸しください

#7

投稿記事 by かずま » 14年前

かずま さんが書きました:

コード:

int is_print(unsigned char c) { return c>=0x20 && c>=0x7e || c>=0xa1 && c<=0xdf; }
c>=0x7e を c<=0x7e に修正してください。

( ゚д゚ )

Re: 再びお知恵をお貸しください

#8

投稿記事 by ( ゚д゚ ) » 14年前

おっしゃる通りfgetcの代わりにfreadでやりたいんです。
現在新しく作り直しているのでまとまったら載せます。
なにかヒントになるようなものがあれば教えてください。

ロード

16進数表示ループ

ASCiiをbufの追加するループ

bufの内容を表示

現在はこんな感じでやっています

へにっくす

Re: 再びお知恵をお貸しください

#9

投稿記事 by へにっくす » 14年前

横レスします。

コード:

int is_sjis1(unsigned char c) { return c>=0x81 && c<=0x9f || c>=0xe0 && c<=0xfc; }
int is_sjis2(unsigned char c) { return c>=0x40 && c<=0xfc && c!=0x7f; }
int is_print(unsigned char c) { return c>=0x20 && c>=0x7e || c>=0xa1 && c<=0xdf; }
余計なお世話かもしれませんが、、
_ismbb系ルーチン (CRT) - MSDN
にある
_ismbblead(マルチバイト文字の第 1 バイト。たとえば、コード ページ 932 に限り、有効な範囲は 0x81 ~ 0x9F および 0xE0 ~ 0xFC です。)
_ismbbtrail(マルチバイト文字の第 2 バイト。たとえば、コード ページ 932 に限り、有効な範囲は 0x40 ~ 0x7E および 0x80 ~ 0xEC です。)
関数は使わないのでしょうかね?
Windows系限定ならばこの関数を使うべきなのかなと
とゆーか、文字判定の関数は標準であるはずですね。
is_printは
isprint、iswprint、_isprint_l、_iswprint_l - MSDN
にありますし。
一度眺めてみることをお勧めします。

かずま

Re: 再びお知恵をお貸しください

#10

投稿記事 by かずま » 14年前

( ゚д゚ ) さんが書きました:おっしゃる通りfgetcの代わりにfreadでやりたいんです。
fgetc の代わりに fread を使ったプログラムです。

コード:

#include <stdio.h>

#define BUF_SIZE 1024

typedef struct {
    FILE *fp;
    unsigned char buf[BUF_SIZE];
    unsigned char *pos;
    int count;
} File;

void init(File *f, FILE *fp)
{
    f->fp = fp,  f->pos = f->buf,  f->count = 0;
}

int get_c(File *f)
{
    if (f->count == 0) {
        f->count = fread(f->buf, 1, BUF_SIZE, f->fp);
        if (f->count == 0) return EOF;
        f->pos = f->buf;
    }
    f->count--;
    return *f->pos++;
}

int unget_c(int c, File *f)
{
    if (f->pos == f->buf) {
        if (f->count > 0) return EOF;
        f->pos = f->buf + BUF_SIZE;
    }
    f->count++;
    return *--f->pos = c;
}
 
int is_sjis1(unsigned char c) { return c>=0x81 && c<=0x9f || c>=0xe0 && c<=0xfc; }
int is_sjis2(unsigned char c) { return c>=0x40 && c<=0xfc && c!=0x7f; }
int is_print(unsigned char c) { return c>=0x20 && c<=0x7e || c>=0xa1 && c<=0xdf; }
 
int get_c2(int c1, File *f)
{
    int c2;
    return is_sjis1(c1) && (c2 = get_c(f)) != EOF &&
        (unget_c(c2, f), is_sjis2(c2)) ? c2 : 0;
}
 
void dump(FILE *fp)
{
    char buf[20];  int i, c1, c2 = 0;  long addr;  File file;
 
    init(&file, fp);
    for (addr = 0; ; addr += 16) {
        for (i = 0; i < 16; i++) {
            if ((c1 = get_c(&file)) == EOF) {
                if (i > 0) printf("%*s  %.*s\n", (16 - i) * 3, "", i, buf);
                return;
            }
            if (i == 0) printf("%08lX  ", addr);
            printf("%02X ", c1);
            if (c2)
                buf[i] = c2, c2 = 0;
            else 
                buf[i] = (c2 = get_c2(c1, &file)) || is_print(c1) ? c1 : '.';
        }
        if (c2) buf[i++] = c2, c2 = '_';
        printf("  %.*s\n", i, buf);
    }
}
 
int main(int argc, char *argv[])
{
    FILE *fp;
    if (argc != 2) { printf("usage: %s file\n", argv[0]); return 1; }
    fp = fopen(argv[1], "rb");
    if (fp == NULL) { printf("can't open %s\n", argv[1]); return 1; }
    dump(fp);
    fclose(fp);
    return 0;
}
ばかばかしい車輪の再発明ですね。
FILE構造体と同様の File構造体を新たに用意し、
fgetc と同様の get_c という関数を作っただけです。

なぜ、fgetc の代わりに fread を使いたいと思ったのでしょうか?
1文字ずつ読むのは効率が悪そうだ。
もっと大量にまとめて読めば効率が良くなるはずだと考えたのではありませんか?

確かにそうですが、それは、標準入出力ライブラリ(stdio.h の FILE) が内部で
バッファを持って提供している機能です。

fgetc, fgets, fscanf, fread のどれを使えばよいのかは、使う側の処理の内容によります。
1バイトずつ処理するのが適当だと思ったら fgetc、
テキストを 1行ずつ処理するのが適当だと思ったら fgets、
テキストを書式に従って変換しながら読み込みたかったら fscanf ということです。

では fread は何のためにあるのでしょうか?
ひとつは、プログラムのメモリ中のデータをそのまま、fwrite で書き出したものを
もう一度読み込むときなどに使います。

double data[50];
n = fread(data, sizeof(double), 50, fp);

struct Hoge data[50];
n = fread(data, sizeof(struct Hoge), 50, fp);

もうひとつは、単なるバイト列としてをまとめて処理したいときです。

char buf[1024];
n = fread(buf, 1, 1024, fp);
hoge(buf, n);

今回のダンププログラムでは、バイト列を読むから fread と考えたのかもしれませんが、
結局は読み込んだバイト列から中身を 1バイトずつ取り出して処理しなければならない
のですから、やっぱり、fgetc を素直に使うほうがよいと思います。

( ゚д゚ )

Re: 再びお知恵をお貸しください

#11

投稿記事 by ( ゚д゚ ) » 14年前

返信が遅くなってしまいもうしわけありませんでした。
おっしゃる通り効率が悪そうなのでfreadを使おうと思いました。

そんな使い分け方があったのですね。
勉強になりました。

一応自分でも組めました(バグ多)

かずまさんぐらいシンプルにプログラムを書けるように勉強をがんばります。

どうもありがとうございました。

( ゚д゚ )

Re: 再びお知恵をお貸しください

#12

投稿記事 by ( ゚д゚ ) » 14年前

解決を押すのをわすれていました。

閉鎖

“C言語何でも質問掲示板” へ戻る