C言語関係掲示板

過去ログ

No853 カンマ区切りのデータの取り込みと変換について

[戻る] [ホームページ]
No.10978

カンマ区切りのデータの取り込みと変換について
投稿者---GGOOD(2003/12/10 09:55:05)


カンマ区切りの31個のデータから1、2、6、7番目のデータを抜き出して
スペース区切りに変換して別のファイルに書き込むプログラムを
作りたいと考えています。

一文字ずつ判定し、カンマがでなければ配列にコピーするような形にしようと思ったのですが、
コンパイルさえ旨くいきません。
strcpyの使い方が間違っているのだろうとは思うのですが、
どのように対処すればよろしいでしょうか。
御教授いただければ幸いです。

下にソースを表示させていただきます。

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


void main(void){
    FILE *o_fp;
    FILE *w_fp;
    char line[255];
    char katashiki[7];
    char ruibetsu[5];
    char katarui[8];
    char car_name[21];
    char engine[21];
    char gomi[255];
    int i,kanma;
    
    o_fp = fopen("test.txt","r");
    while(fgets(line,256,o_fp)){
        for(i=0;i>0;i++){
            switch(kanma){
                case 0:
                    if(line[i] == ','){
                        kanma++;
                        }else{
                        strcpy(katashiki,line[i]);
                        }
                    break;

                case 1:
                    if(line[i] == ','){
                        kanma++;
                        }else{
                        strcpy(ruibetsu,line[i]);
                        }
                    break;

                case 6:
                    if(line[i] == ','){
                        kanma++;
                    }else{
                        strcpy(car_name,line[i]);
                        }
                    break;

                case 7:
                    if(line[i] == ','){
                        kanma++;
                        }else{
                        strcpy(engine,line[i]);
                        }
                    break;

                default:
                    if(line[i] == ','){
                        kanma++;
                        }else{
                        strcpy(gomi,line[i]);
                        }
                    break;
            }
        }
    }
    
    w_fp = fopen("result.txt","w");
    fprintf(w_fp,"%s %s %s %s %s",katashiki,ruibetsu,car_name,engine);
    fclose(w_fp);
}

ご指導、よろしくお願いします。

No.10981

Re:カンマ区切りのデータの取り込みと変換について
投稿者---おでん(2003/12/10 10:08:13)


> for(i=0;i>0;i++){
> switch(kanma){

ちょっと見でまず気が付くのは、
iに0を入れているのに、0以上の判断をしている。
→ループの中に入りません。
kanmaの初期化がされていない。
→デフォルトに行ってしまいます。

あとstrcpy()の使い方ですが、パラメータはアドレス渡しですから
strcpy(katashiki,line[i]);時は、strcpy(katashiki,&line[i]);とします。
・・・投稿されたソースの処理が合ってるかどうかは未確認です。

No.10992

Re:カンマ区切りのデータの取り込みと変換について
投稿者---GGOOD(2003/12/10 15:50:06)


レスありがとうございます。

ご指摘の点、修正したところ、コンパイルがとおるようになりました。
ありがとうございます。

ですが、実行したところ、一般保護違反が出てしまい、
ファイルの書き込み(生成)も行われませんでした。

strcpy(katarui[i],&line[i])という書き方が根本的におかしいのでしょうか?
カンマまでの文字数を拾ってきてそこから代入したほうが正解なのでしょうか?

御教授いただきたく、書き込みさせていただきました。
よろしくお願いします。

No.10995

Re:カンマ区切りのデータの取り込みと変換について
投稿者---おでん(2003/12/10 16:46:23)


>ですが、実行したところ、一般保護違反が出てしまい、
>ファイルの書き込み(生成)も行われませんでした。
>
>strcpy(katarui[i],&line[i])という書き方が根本的におかしいのでしょうか?

まず、strcpy(katarui[i],&line[i])では無くstrcpy(katarui,&line[i])です。

またstrcpy()は、ソース(&line[i])にある文字列を全てkataruiに
コピーします。従って、上記の例ですとコピーされる文字数が、
kataruiのサイズよりも多い時には他の領域を壊してしまいます。

>カンマまでの文字数を拾ってきてそこから代入したほうが正解なのでしょうか?

考え方だと思うのですが、カンマにはさまれた文字列を取り出す関数を
作って、カンマを見つけたらその関数を呼ぶようにしたらどうでしょう?
当然行の最初にはカンマが無い(ですよね?)のでそういったことへの
対処も必要だと思います。

No.11011

Re:カンマ区切りのデータの取り込みと変換について
投稿者---GGOOD(2003/12/10 19:20:07)


レスありがとうございます。
調べてみたところ、CSV形式のファイルを読み出す関数を教えているページが有りましたので、
それを応用して、以下のように作り直してみました。

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

#define LINE_SIZE 1024
#define ITEM_COUNT 31
#define ITEM_SIZE 30

char *data_cut(char *result , char *buffer , int size);

void main(void){

    FILE *r_fp;
    FILE *w_fp;
    char buffer[LINE_SIZE];
    char *result;
    char item[ITEM_COUNT][ITEM_SIZE];
    int i;
    int length;

    r_fp = fopen("test.txt","r");
    for(;;){
        if(fgets(buffer,LINE_SIZE,r_fp)==NULL){
            break;
        }
        length = strlen(buffer);
        if(length == 0){
            break;
        }
        result = buffer;
        
        for(i=0;i<ITEM_COUNT;i++){
            result = data_cut(result,item[i],ITEM_SIZE);
        }

    
        w_fp = fopen("result.txt","w+");

        fprintf(w_fp,"%s ",item[0]);
        fprintf(w_fp,"%s ",item[1]);
        fprintf(w_fp,"%s ",item[6]);
        fprintf(w_fp,"%s\n",item[7]);
                
        fclose(r_fp);
    }

}

char *data_cut(char *result,char *buffer,int size){
    int i;
    
    buffer[0] = '\0';
    for(i=0;i<ITEM_SIZE;i++,result++){
        buffer[i] = *result;
        if(*result == '\0'){
             buffer[i] = '\0';
             return(result);
        }

        if(*result == ','){
            result++;
            buffer[i] = '\0';
            break;
        }
    }
    return(result);
}


コンパイルも実行も成功したのですが、一行目しか書き込みされていませんでした。
NykRさんに御教授いただきましたソースで旨くいくとは思うのですが、
せっかく自分で作ってみましたので、できればきちんと動作するようにしてみたいとおもいまして・・・
よろしければまた添削いただけませんでしょうか?
よろしくお願いします。

No.11013

Re:カンマ区切りのデータの取り込みと変換について
投稿者---NykR(2003/12/10 20:47:03)


>w_fp = fopen("result.txt","w+");
ループの外で"w"で開く

>fclose(r_fp);
外に出す

で多分うまくいくと思います。

・他

ループを抜けたところで、w_fpを閉じる。

×void main(void){/*.....................*/}
○int main(void){/*.....................*/ return 0;}

if(*result == '\0'){
     buffer[i] = '\0';<---いらない
     return(result);
}


data_cutはstrchrやsprintfを使うともっと簡単にできます。
また、itemをポインタの配列にしてstrtok関数を使うという手もあります(bufferの','が'\0'に置き換わるけど)。

No.11027

Re:カンマ区切りのデータの取り込みと変換について
投稿者---GGOOD(2003/12/11 09:05:56)


おはようございます。
昨日はありがとうございました。

おっしゃるように変更して実行してみたところ、
望むとおりの結果が返ってきました。

また、何か質問させていただく事が有るかもしれませんが、
その時はまたよろしくお願いします。

No.11006

Re:カンマ区切りのデータの取り込みと変換について
投稿者---NykR(2003/12/10 17:57:33)


>一文字ずつ判定し、カンマがでなければ配列にコピーする

一文字ずつ判定し、カンマでなければ配列にコピーする
という方法を採るのであれば、strcpyを使うこと自体間違っています
char *p;
/*...*/
i = 0, kanma = 0;
for (p = line; *p != '\0'; p++) {
    switch (kanma) {
    case 0:
        if (*p == ',') {
            kanma++;
            i = 0;
        } else {
            katashiki[i] = *p;   /* 最後にNUL文字を入れないといけないよ */
        }
    case /*...
       ...*/
    }
}

ところで、一々配列にコピーする必要があるのでしょうか?(使い回してるし)
これで十分な気がしますが。
#include <stdio.h>

int main(void)
{
    FILE *o_fp, *w_fp;
    char line[1024], *line_header;
    int kanma = 0;

    o_fp = fopen("test.txt", "r");
    w_fp = fopen("result.txt", "w");
    while (fgets(line, sizeof(line), o_fp) != NULL) {
        kanma = 0;
        for (line_header = line; *line_header != '\0'; line_header++) {
            switch (kanma) {
            case 0:
            case 1:
            case 6:
            case 7:
                if (*line_header == ',') putc(' ', w_fp);
                else putc(*line_header, w_fp);
                break;
            }
            if (*line_header == ',') kanma++;
        }
        putc('\n', w_fp);
    }
    fclose(o_fp);
    fclose(w_fp);

    return 0;
}


No.11010

Re:カンマ区切りのデータの取り込みと変換について
投稿者---GGOOD(2003/12/10 19:12:46)


レスありがとうございます。なるほど!そういう方法がありますね。

カンマ区切りの時点で「配列でやらな・・・」という意識になってしまっていました。

試させていただきます、ありがとうございました。

No.11014

Re:カンマ区切りのデータの取り込みと変換について
投稿者---GGOOD(2003/12/10 20:47:21)


帰宅後ためさせていただきましたところ、
うまく書き出しできていました。

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

ただ、kanmaがmain関数中で使用されていないというような警告が
コンパイル時に表示されます。

実行には問題ないのですが、何故表示されるのでしょう?
どう見ても使用されると思うのですが。

No.11016

Re:カンマ区切りのデータの取り込みと変換について
投稿者---NykR(2003/12/10 20:59:36)


while (fgets(line, sizeof(line), o_fp) != NULL) {
    kanma = 0;
が
while (fgets(line, sizeof(line), o_fp) != NULL) {
    int kanma = 0;

になってませんか?
もしそうなってたら、ここのintを消すか、
外側のint kanma = 0;を消してください。

#それ以外の理由は思い当たらない

No.11018

Re:カンマ区切りのデータの取り込みと変換について
投稿者---GGOOD(2003/12/10 21:06:54)


    FILE *o_fp, *w_fp;
    char line[1024], *line_header;
    int kanma = 0;

    while (fgets(line, sizeof(line), o_fp) != NULL) {
        kanma = 0;


このようになっていました。
初期化は別に複数回やっても問題ないでしょうし
何ででしょうね?

ちなみに、OSはWin95 Borland C++Builder 5.5を使用しています。

No.11021

Re:カンマ区切りのデータの取り込みと変換について
投稿者---RAPT(2003/12/10 23:05:01)


本題とは、ずれますが、
> int kanma = 0;
これは初期化ですが、
> kanma = 0;
これは初期化ではありません。「代入」です。

warningを消すには、どこか(whileブロック外)に、
kanma;
と入れておけばOKだと思います。

No.11028

Re:カンマ区切りのデータの取り込みと変換について
投稿者---GGOOD(2003/12/11 09:07:42)


>これは初期化ではありません。「代入」です。

そうでした(汗
もうちょっと勉強しますw

警告は出なくなりました、ありがとうございました。

No.11023

Re:カンマ区切りのデータの取り込みと変換について
投稿者---nop(2003/12/11 00:05:15)


>カンマ区切りの31個のデータから1、2、6、7番目のデータを抜き出して
>スペース区切りに変換して別のファイルに書き込むプログラムを
>作りたいと考えています。

こんな方法もある。

#include    <stdio.h>

int main( void )
{
    int  extracts[] = { 1, 2, 6, 7 };
    FILE    *fp_from;
    FILE    *fp_to;
    int  ch;
    int  i,j;

    fp_from = fopen( "in.txt", "r" );
    fp_to   = fopen( "out.txt", "w" );

    for( i=j=0; !feof(fp_from) && i<sizeof(extracts)/sizeof(extracts[0]); j+=(ch==',') )
    {
        ch = fgetc( fp_from );

        if( extracts[i]==j+1 && ch!=EOF )
        {
            fputc( (ch==',')?' ':ch, fp_to );
            i += (ch==',');
        }
    }
    fclose( fp_from );
    fclose( fp_to );
}



No.11029

Re:カンマ区切りのデータの取り込みと変換について
投稿者---GGOOD(2003/12/11 09:09:46)


レスありがとうございます。

うーん・・・ ぱっと見いきなりよく分かりません(汗
ちょっとリファレンスとにらめっこしつつ勉強してみます。

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

No.11036

配列サイズの計算
投稿者---ゆかり(2003/12/11 11:50:27)


本題とは何も関係ない話で割り込んですみません。

>for( i=j=0; !feof(fp_from) && i<sizeof(extracts)/sizeof(extracts[0]); j+=(ch==',') )

こういうループをよく見ますが、

ookisa=sizeof(extracts)/sizeof(extracts[0]);
for( i=j=0; !feof(fp_from) && i<ookisa; j+=(ch==',') )

↑このようにした方が効率良くないですか?


No.11040

Re:配列サイズの計算
投稿者---たいちう(2003/12/11 13:27:47)


アルゴリズムを根本的に見直す必要があるような場合は別ですが、
最近のコンパイラは効率が良くなるようにコンパイルしてくれるので、
多分効率は変わらないでしょう。

コンパイラが賢くなかったとしても、ookisaの計算をループの
外に出すことで、どの位の処理時間の短縮になると思いますか?
ループの本体の処理時間と比較すると取るに足らない場合が
ほとんどだと思います。

可読性を高めるために書き換えたいということならば、お好みで。

No.11044

Re:配列サイズの計算
投稿者---たか(2003/12/11 14:12:24)


>本題とは何も関係ない話で割り込んですみません。
>
>>for( i=j=0; !feof(fp_from) && i<sizeof(extracts)/sizeof(extracts[0]); j+=(ch==',') )
>
>こういうループをよく見ますが、
>
>ookisa=sizeof(extracts)/sizeof(extracts[0]);
>for( i=j=0; !feof(fp_from) && i<ookisa; j+=(ch==',') )
>
>↑このようにした方が効率良くないですか?

もちろんコンパイラの仕事を手伝うのであればそちらの方がいいでしょ
う。sizeof(extracts)とsizeof(extracts[0])はコンパイル時には定数な
ので恐らく最適化によって大抵のコンパイラでは同等のコードを吐くとは
思いますが。

No.11128

Re:配列サイズの計算
投稿者---ゆかり(2003/12/12 17:49:51)


>sizeof(extracts)とsizeof(extracts[0])はコンパイル時には定数な
>ので恐らく最適化によって大抵のコンパイラでは同等のコードを吐くとは
>思いますが。

定数なんですか。知りませんでした。
定数だったら効率は変わらないわけですね。

ただたいちうさんのおっしゃるように、
読みやすいものを心がけていますので、私は、
やはりループの外で計算すると思います。

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

No.11134

Re:配列サイズの計算
投稿者---NykR(2003/12/12 19:03:49)


>ただたいちうさんのおっしゃるように、
>読みやすいものを心がけていますので、私は、
>やはりループの外で計算すると思います。

マクロにするという手もあるんじゃないかと思います。
ポインタを渡されると困りますが
そういう人はsizeof演算子を使うときも同じことをやりそうだし、
直接書いたりループの外で計算するよりも
見た目に楽な気がする。

No.11140

Re:配列サイズの計算
投稿者---RAPT(2003/12/13 01:24:45)


私の場合は、以下のようなマクロを定義して使用することが多いですね。
#define countof(array) (sizeof(array)/sizeof(array[0]))
for(i=0;i<countof(s);++i){}

関数呼び出しなら、
const int len = strlen(s);
for(i=0; i<len;++i){}
こんな風にしますが。