掲示板利用宣言

 次のフォームをすべてチェックしてからご利用ください。

 私は

 題名と投稿者名は具体的に書きます。
 課題の丸投げはしません。
 ソースの添付は「HTML変換ツール」で字下げします。
 返信の引用は最小限にします。
 環境(OSとコンパイラ)や症状は具体的に詳しく書きます。
 返信の付いた投稿は削除しません。
 マルチポスト(多重投稿)はしません。

掲示板2

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧

No.28072

ランレングス圧縮のプログラム
投稿者---みんとらいむ(2006/09/07 21:16:22)


下のプログラムはファイルを読み込んでランレングス圧縮するプログラムです。色々調べながら私なりにプログラムを書いてみたのですが、表示データの後に文字化けした記号が追加されてしまいます。(フフフフフ・・・等)

原因が特定できないので、アドバイスとか頂ければとても助かります。
コメントなど間違って認識して、書いてるところとかもありますが・・・。
初めての投稿で至らない点も多いと思いますが、よろしくお願いします。

[開発環境:microsoft visual C++ 2005 Express Editon]


#include<stdio.h>
#include<string.h>

int main(void)
{
/* s[100]:圧縮する前の文字列 */
/* p[]:圧縮された後の文字列 */
/* q[]:圧縮する文字の数 */
char s[100], p[100], q[20];
int i, j, c;
int ch;
FILE *fp;
char fname[64]; //ファイル名

printf("ファイル名:"); //ファイル名
scanf("%s", fname);

if ((fp = fopen(fname, "rb")) == NULL)
printf("ファイルをオープンできません。\n");

else {
fread(s, sizeof(char), 100, fp);

i = 0; // 配列sの番地
j = 0; // 配列pの番地
c = 1;

while(s[i] != '\0'){
i++;
if(s[i - 1] != s[i]){ /* 1文字前と文字が違えば */
if(c <= 2){ /* c = 1 or 2のとき */
while(c != 0){
p[j] = s[i - c];
c--;
j++;
}
}
else{
p[j] = '$'; /* P[j](圧縮した文字列の最初に$をつける) */
sprintf(q, "%d", c); /* qにcをいれて、文字列の最後に\0を付加して出力する */
p[j + 1] = q[0]; /* p[1] = q[0] */
p[j + 2] = s[i - 1]; /* p[2] = s[i - 1] */
j = j + 3;
}
c = 1;
}

else{
c++;
}

}

q[c] = '\0';
p[j] = '\0';

printf("*圧縮前*\n%s", s);
fread(p, sizeof(char), 100, fp);
printf("\n*圧縮後*\n%s", p);


fclose(fp);
}

fflush(stdin);
getchar();
return(0);
}


この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:ランレングス圧縮のプログラム 28074 nano 2006/09/07 21:44:21
<子記事> Re:ランレングス圧縮のプログラム 28075 nano 2006/09/07 22:16:27
<子記事> Re:ランレングス圧縮のプログラム 28084 yoh2 2006/09/08 20:20:58


No.28074

Re:ランレングス圧縮のプログラム
投稿者---nano(2006/09/07 21:44:21)


ソースコードをアップロードする際は、HTML変換ツールを使ってください。

>   while(s[i] != '\0'){
>      i++;
>      if(s[i - 1] != s[i]){    /* 1文字前と文字が違えば */

実体とコメントとが食い違っているように見えます。
2行目で i をインクリメントしていることにより、
1文字前と自分との比較ではなく、自分と1文字後ろとを
比較しているように見えます。
本当に行ないたいことはどちらなのでしょうか?
>   printf("*圧縮前*\n%s", s);
>   fread(p, sizeof(char), 100, fp);
>   printf("\n*圧縮後*\n%s", p);

ここのfread関数でpに読み込むことで、
それまでセットしてきたpの内容が壊れていませんか?


この投稿にコメントする

削除パスワード

No.28076

Re:ランレングス圧縮のプログラム
投稿者---みんとらいむ(2006/09/07 22:16:29)


>実体とコメントとが食い違っているように見えます。
>2行目で i をインクリメントしていることにより、
>1文字前と自分との比較ではなく、自分と1文字後ろとを
>比較しているように見えます。
>本当に行ないたいことはどちらなのでしょうか?

迅速な返信ありがとうございます。
指摘していただいたとおりコメントと食い違ってますね・・・><
私のしたかったことは一文字後ろと比較することです・・・。コメントが間違っています・・・。


><pre>> printf("*圧縮前*\n%s", s);
> fread(p, sizeof(char), 100, fp);
> printf("\n*圧縮後*\n%s", p);
</pre>

>ここのfread関数でpに読み込むことで、
>それまでセットしてきたpの内容が壊れていませんか?

なるほど・・・。しかし、実行結果では表示自体は行われていて、その後に変な文字が入るので内容全部が壊れているわけではないようなのですが・・・。
どこから壊れているのか考えなおしてみます。アドバイスありがとうございます。本当に助かります^^



この投稿にコメントする

削除パスワード

No.28079

Re:ランレングス圧縮のプログラム
投稿者---nano(2006/09/07 22:37:58)


>なるほど・・・。しかし、実行結果では表示自体は行われていて、その後に変な文字が入るので内容全部が壊れているわけではないようなのですが・・・。

2回目のfreadで、pの先頭から100バイトは圧縮しない状態になっています。
その直後のprintfで、圧縮後のつもりでpを出力しても、
その内容には圧縮していない部分を含んでいます。それは正しいでしょうか?



この投稿にコメントする

削除パスワード

No.28080

Re:ランレングス圧縮のプログラム
投稿者---みんとらいむ(2006/09/07 22:44:59)



正しいと思います。ファイルから出力するということをほとんどしたことがないので自信を持っていえないですが・・・^^;
度々のご指摘ありがとうございます。



この投稿にコメントする

削除パスワード

No.28081

Re:ランレングス圧縮のプログラム
投稿者---nano(2006/09/07 23:11:49)


>正しいと思います。

そうなんですか…。

[2回目のfread後のp]
+----------------------+--------------------+
|  圧縮していない状態  |  圧縮している状態  |
|  (通常100バイト)   |  (0バイト以上)   |
+----------------------+--------------------+

この全体を、「圧縮している状態」と呼べるというわけなんですね。
私はそうは思わないんですが、ランレングスについて詳しくない私が
言うことには全く説得力がないですね。


この投稿にコメントする

削除パスワード

No.28082

Re:ランレングス圧縮のプログラム
投稿者---みんとらいむ(2006/09/07 23:33:15)


丁寧に図まで書いていただきありがとうございます。
その説明を踏まえて、修正してみます。
色々と勉強になりました。本当にありがとうございます。





この投稿にコメントする

削除パスワード

No.28085

Re:ランレングス圧縮のプログラム
投稿者---yoh2(2006/09/08 20:31:59)


>
>正しいと思います。ファイルから出力するということをほとんどしたことがないので自信を持っていえないですが・・・^^;
>度々のご指摘ありがとうございます。

2度目のfread()の前にfseek()等で読み込み位置を戻してはいないため、
ファイル容量が100バイト以下(つまり、1回目のfread()でファイルを全て読み込んだ)
の場合、2度目のfread()ではpに何も書き込まれないため、*たまたま*正しく動いて
いるように見えるだけです。
ファイル容量が100バイトを超えた場合、2度目のfread()で、1度目に読み切れなかった
データでpが上書きされて、pの内容が壊れます。

そもそも2度目のfread()の意図が不明です。

結論: 2度目のfread()は余計

だと思うのですが、いかがでしょう。


この投稿にコメントする

削除パスワード

No.28075

Re:ランレングス圧縮のプログラム
投稿者---nano(2006/09/07 22:16:27)


ファイル入出力関連で言いますと、
fread関数を2回実行するだけですが、それでよいのでしょうか?
ループを回したり別のファイルに書き込んだりする必要はないのですか?


この投稿にコメントする

削除パスワード

No.28078

Re:ランレングス圧縮のプログラム
投稿者---みんとらいむ(2006/09/07 22:20:59)



nanoさんのおっしゃるように、別のファイルに最終的には書き込めるようなプログラムになればいいなぁと思い作成しています。
まだ、試作段階なので今はファイルの情報を見るだけに留めています・・・


この投稿にコメントする

削除パスワード

No.28084

Re:ランレングス圧縮のプログラム
投稿者---yoh2(2006/09/08 20:20:58)


>	while(s[i] != '\0'){

scanf()やfgets()と異なり、fread()で読み込んだデータは'\0'終端されないので、
現状のプログラムsが'\0'で終端されるためには、ファイルに書かれている
データが'\0'で終端していなくてはなりません。これは大丈夫ですか?
単なるテキストファイルの最後には'\0'は付きませんよ。
それに、'\0'で終わっていたとしても、ファイルに100文字以上あると
その'\0'が読み込まれません (これは分かった上で単純化のために無視してるのかな)。

ファイルが'\0'で終わっていない場合の対策。
その1: fread()の戻り値(読み込んだデータ数)を使う。
  size_t read_size;
  ...
  read_size = fread(s, sizeof(char), 99, fp);
  s[read_size] = '\0';

その2: sをゼロクリアしておく。
  char s[100] = {0}, p[100], q[20];
  ...
  fread(s, sizeof(char), 99, fp);

どちらの場合も、'\0'のための領域を取っておくために第3引数を99にする必要があります。
ちなみに私の好みはその1の方。


その他気になること。
1. 圧縮前の文字列に'$'がある場合、圧縮結果を元に戻せなくなる。
入力が"$5a"の場合と、"aaaaa"の場合、どちらも圧縮結果が"$5a"になります。
入力文字に'$'が入らないという仕様なら問題ないのですが。

2. 10文字以上連続するとおかしな結果となる。
入力が"aaaaaaaaaaaaaaa"の場合、"$1a"になってしまいます。
対策としては、
> if(s[i - 1] != s[i]){ /* 1文字前と文字が違えば */

に条件を追加して、すでに9文字連続している場合にも条件に引っかかるようにするとか。
その場合、"aaaaaaaaaaaaaaa"を圧縮すると"$9a$6a"になります。


この投稿にコメントする

削除パスワード

No.28087

Re:ランレングス圧縮のプログラム
投稿者---みんとらいむ(2006/09/11 13:12:57)


すみません。旅行にでかけてたため返信が遅くなりました。
yoh2さんのご指摘どおり、ファイルが'\0'で終わっていない場合の対策を行っていませんでした。
9文字以上同じ文字が入った場合、実行結果がおかしくなるので9文字以内までという条件を加えようべきですよね・・・。
"$5a"というのと"aaaaa"という結果がおなじになる件についてはあとで、条件文を加えて制御しようと考えています。できるかどうかはわかりませんが・・・^^;

色々なアドバイス本当にありがとうございます^^ このアドバイスを参考に改善してみます。


この投稿にコメントする

削除パスワード

No.28127

Re:ランレングス圧縮のプログラム
投稿者---みんとらいむ(2006/09/12 17:27:18)


一応形になりましたので、乗せてみます。見にくい部分とかあると思いますが、ご容赦ください。nanoさん、yoh2さん質問に答えていただき本当にありがとうございました。
yoh2さんに指摘していただいた9文字以上の場合正しく表示されないなどの問題点はまだ残ってますが・・・^^;

/******************************************/
/******* ランレングス圧縮プログラム *******/
/*******          ver1.06           *******/
/******************************************/


#include<stdio.h>
#include<string.h>


int main(void)
{
    /* s[100]:圧縮する前の文字列 */
    /* p[]:圧縮された後の文字列 */
    /* q[]:圧縮する文字の数 */
    
    char s[100] = {0}, p[100] = {0}, q[20];  //Sをゼロクリア
    int   i, j, c;
    int   ch;
    FILE     *sfp, *dfp;
    char     sname[64], fname[64];  //読込みファイル名, 書込みファイル名

    printf("読出しファイル名:");        //読込みファイル名
    scanf("%s", sname);

    printf("書込みファイル名:");        //書込みファイル名
    scanf("%s", fname);


    if ((sfp = fopen(sname, "rb")) == NULL)    //ファイルオープン(read)
        printf("ファイルをオープンできません。\n");
    else{
        fread(s, sizeof(char), 99, sfp);
        
        if ((dfp = fopen(fname, "wb")) == NULL)
            printf("圧縮データを書き込むファイルをオープン出来ません。");

        else {
    
            i = 0;    // 配列sの番地
            j = 0;    // 配列pの番地
            c = 1;

            while(s[i] != '\0'){
                i++;
                
                if(s[i] == '$'){                                                /* 文字列に"$"があれば圧縮しない */
                            printf("$を含むデータは圧縮できません.\n");
                            
                            break;         
                           }

                if(s[i - 1] != s[i]){      /* 自分と1文字後を比較して */
                    if(c <= 2){    /* c = 1 or 2のとき(同じ文字が1〜2個続くとき) */
                        while(c != 0){
                            p[j] = s[i - c];
                            c--;
                            j++;
                                 } 
                            }
                    else{            
                        p[j] = '$';    /* P[j](圧縮した文字列の最初に$をつける) */
                        sprintf(q, "%d", c);    /* qにcをいれて、文字列の最後に\0を付加して出力する */
                        p[j + 1] = q[0];        /* p[1] = q[0] */
                        p[j + 2] = s[i - 1];    /* p[2] = s[i - 1] */
                        j = j + 3;
                        }
                    c = 1;
                                    }
        
            else{
                    c++;
                }
            
                        }
        

    q[c] = '\0';
    p[j] = '\0';

    printf("*圧縮前*\n%s\n", s);
    
    fwrite(p, sizeof(char), 99, dfp);

    fclose(sfp);        /* クローズ */
    
    fclose(dfp);
        }
    }
    fflush(stdin);
    getchar();
    return(0);
}



この投稿にコメントする

削除パスワード

No.28137

Re:ランレングス圧縮のプログラム
投稿者---yoh2(2006/09/13 20:52:50)


最後の部分
>     printf("*圧縮前*\n%s\n", s);
>     
>     fwrite(p, sizeof(char), 99, dfp);

fwriteの第3引数 (99) はマズいですね。圧縮後の文字列を越えた領域にあるゴミまで
書き出してしまいます。
ここは実際の変換後の文字数を書き出すようにしましょう。

(別解) fwrite()ではなくfprintf()を使う。文字数の指定が不要になります。

念のため注意1: fprintf()を使った場合、末尾の'\0'は書き出されません。
fwrite()なら第3引数次第。
念のため注意2: 普通のテキストファイルとして出力したいのなら、'\0'を書き出してはいけません。

# いや、前のほうの書き込みで、ファイルが'\0'で終わっていることを期待しているように感じたもので。


この投稿にコメントする

削除パスワード

No.28145

Re:ランレングス圧縮のプログラム
投稿者---shu(2006/09/13 22:35:51)


//
//	サンプルプログラム(圧縮部分のみ)
//

//
#include <stdio.h>
#include <stdlib.h>

//
#define Nj	9

//
int main(void)
{
    char s[BUF_SIZ] = "aaaaaaaaaaaaabbcdddeeeaasaaagagassss";
    int i, j;
    
    //
    puts(s);
    
    for (i = 0; s[i]; i += j) {
        for (j = 0; s[i] == s[i + j]; j++) {
            if (j >= Nj) {
                break;
            }
        }
        printf("$%c%c", s[i], j + '0');
    }
    return 0;
}



この投稿にコメントする

削除パスワード

No.28151

Re:ランレングス圧縮のプログラム
投稿者---shu(2006/09/14 14:36:20)


//
//	出力パターンいろいろ
//

//
#include <stdio.h>
#include <stdlib.h>

//
#define Nj	9

//
int main(void)
{
    char s[] = "aaaaaaaaaaaaaaabbbbbbbbbbbbbbbccccccccccccccc";
    int i, j;
    
    //
    puts(s);
    
    for (i = 0; s[i]; i += j) {
        for (j = 0; s[i] == s[i + j]; j++) {
            if (j >= Nj) {
                break;
            }
        }
        //	一文字ずつ出力
        putchar('$');
        putchar(s[i]);
        putchar(j + '0');
        
        //	一文字ずつ代入
        //	s2[k++] = '$';
        //	s2[k++] = s[i];
        //	s2[k++] = j + '0';
        
        //	一文字ずつファイル出力
        //	putc('$', fp);
        //	putc(s[i], fp);
        //	putc(j + '0', fp);
        
        //	標準出力
        //	printf("$%c%d", s[i], j);
        
        //	文字列出力
        //	sprintf(s2 + k, "$%c%d", s[i], j);
        //	k += 3;	//	$, 文字, 個数の3文字分
        
        //	ファイル出力
        //	fprintf(fp, "$%c%d", s[i], j);
    }
    //
    return 0;
}


データのよっては圧縮後の方がデータサイズが大きくなり、
さらに別の文字配列に代入する場合、用意したサイズを越えることもある。



この投稿にコメントする

削除パスワード

No.28149

Re:ランレングス圧縮のプログラム
投稿者---みんとらいむ(2006/09/14 09:27:36)


なるほど・・・。確かにyoh2さんの仰るとおり、"\0"で終わることを期待していました。ご指摘ありがとうございます。
fprintfを使ったプログラムに改良してみます。
shuさん、サンプルプログラムを載せていただきありがとうございます。このサンプルを見る限り9文字以上でも正しく動作するようですね。
お二人の意見をもとにもう少し改良を加えようと思います。書込みしていただき、ありがとうございます^^


この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧