No.16232![]() |
「""」で囲まれたCSV形式データの読み込み 投稿者---やもと(2004/08/18 12:17:45) |
||
文字列がダブルクォーテーションで囲まれたカンマ区切りのCSV形式データ からダブルクォーテーションを取り除いた文字列だけを取得しようと しています。文字列中にカンマが含まれている場合はカンマは有効とします。 データの4行目と5行目にある通り、カンマが文字列の前にあると上手く 取得できません。 どうれば上手くできますか。 -- 読み込みデータ ----- "000001","CODE1","2002.08.26","a1 a2 a3" "000002","","2002.08.26","" "000003","","2002.08.26","a1,a2" "000004","CODE2","2002.08.26",",a1" "000005",",CODE2","2002.08.26","a1" "000006","","2002.08.26","a1," -- 結果 ---- :000001:CODE1:2002.08.26:a1 a2 a3: :000002::2002.08.26:: :000003::2002.08.26:a1,a2: :000004:CODE2:2002.08.26:: → :000004:CODE2:2002.08.26:,a1: を期待 :000005::CODE2:2002.08.26: → :000005:,CODE2:2002.08.26:a1: を期待 :000006::2002.08.26:a1,: -- ソース ---- #include <stdio.h> #include <string.h> char *setvalue(char *, char *, int) ; typedef struct { char str1[64] ; char str2[64] ; char str3[64] ; char str4[64] ; } TEMP_DATA ; main() { FILE *fp ; TEMP_DATA temp_data ; char buff[1024], *p ; memset(buff,'\0',sizeof(buff)) ; while(fgets(buff,sizeof(buff),stdin)!=NULL) { p = setvalue(buff, temp_data.str1, sizeof(temp_data.str1)) ; p = setvalue(p+2, temp_data.str2, sizeof(temp_data.str2)) ; p = setvalue(p+2, temp_data.str3, sizeof(temp_data.str3)) ; p = setvalue(p+2, temp_data.str4, sizeof(temp_data.str4)) ; printf(":%s:%s:%s:%s:\n", temp_data.str1, temp_data.str2, temp_data.str3, temp_data.str4) ; } return 0 ; } char *setvalue(s, t, size) char *s ; char *t ; int size ; { char *e ; e = t + size - 1 ; while (t < e && *s != '\0' && *s != '\n') { if (*s == '"' && *(s+1) != ',') { *s++ ; continue ; } if (*s == '"' && *(s+1) == ',') { break ; } *t++ = *s++ ; } *t = '\0' ; return s ; } |
No.16233![]() |
Re:「""」で囲まれたCSV形式データの読み込み 投稿者---Sciggepy(2004/08/18 12:47:42) |
||
<pre>> if (*s == '"' && *(s+1) != ',') { > *s++ ; > continue ; > } > if (*s == '"' && *(s+1) == ',') { > break ; > } </pre>'"'の隣に','があるかどうかで分岐していますよね。これでは、 ",???"という形のデータを読み込もうとした際、",の部分でループを脱出してしまい、次の呼び出しでは、???の部分が読み込まれてしまいます。 最初の'"'を読み込んだら、最後の'"'を読み込むまでループを脱出しないようにする必要があります。'"'を文字として読み込むには、'\"'とする必要もあるかと思います。 |
No.16234![]() |
Reできました。少し良い方法はないでようか。 投稿者---やもと(2004/08/18 14:29:02) |
||
ありがとうございます。 書込みを元に考えてみました。 なんとか上手く動くようになりました。 書き方というか、見栄えというか、もう少し良い処理が出来るのでは ないかと思うのですが、なにかないでしょうか。 char *setvalue(s, t, size) char *s ; char *t ; int size ; { char *e ; int flg = 0 ; e = t + size - 1 ; while (t < e && *s != '\0' && *s != '\n') { if (*s == '\"' && flg == 0) { flg = 1 ; *s++ ; continue ; } if (*s == '\"' && *(s+1) == ',' && *(s+2) == '\"') { break ; } if (*s == '\"' && *(s+1) == '\n') { break ; } *t++ = *s++ ; } *t = '\0' ; return s ; } |
No.16235![]() |
Re:Reできました。少し良い方法はないでようか。 投稿者---nop(2004/08/18 15:24:14) |
||
詳細なデバッグはしてないが、 こんな感じでいかがだろう? ------------------------------------------------------------ #include <stdio.h> #include <string.h> char *setvalue(char *, char *, int); typedef struct { char str1[64]; char str2[64]; char str3[64]; char str4[64]; } TEMP_DATA; int main( void ) { FILE *fp; TEMP_DATA temp_data; char buff[1024], *p; memset(buff,'\0',sizeof(buff)); while(fgets(buff,sizeof(buff),stdin)!=NULL) { /* ----- 改行を削除 ----- */ ( p=strrchr(buff,'\n') ) ? *p='\0': 0; p = setvalue( buff, temp_data.str1, sizeof(temp_data.str1) ); p = setvalue( p, temp_data.str2, sizeof(temp_data.str2) ); p = setvalue( p, temp_data.str3, sizeof(temp_data.str3) ); p = setvalue( p, temp_data.str4, sizeof(temp_data.str4) ); printf( ":%s:%s:%s:%s:\n", temp_data.str1, temp_data.str2, temp_data.str3, temp_data.str4); } return 0; } char *setvalue( char *s, char *t, int size ) { /* ----- 内部変数定義 ----- */ char iFlag = 0; int i; for( i=0; i<size && *s && !(!iFlag && *s==','); i++,s++ ) { switch( *s ) /* 文字判定 */ { case '\"': /* ----- フラグを反転 ----- */ iFlag = !iFlag; break; case ',': if( iFlag ) /* 「"」で括られた文字列外の「,」か? */ { /* ----- 文字をコピー ----- */ *t++ = *s; } break; case '\\': /* ----- 次の文字へ ----- */ s++; default: /* ----- 文字をコピー ----- */ *t++ = *s; break; } } /* ----- ヌルターミネート ----- */ *t = '\0'; return s+(*s==','); } |
No.16239![]() |
Re:Reできました。少し良い方法はないでようか。 投稿者---やもと(2004/08/18 22:26:51) |
||
ありがとうございます。 このような処理の方法もあるのですね。 一例として載せていただいたソースのロジックについて 教えて下さい。 ( p=strrchr(buff,'\n') ) ? *p='\0': 0; for( i=0; i<size && *s && !(!iFlag && *s==','); i++,s++ ) return s+(*s==','); の3箇所の使い方、動きについて教えて下さい。 |
No.16240![]() |
Re:Reできました。少し良い方法はないでようか。 投稿者---RAPT(2004/08/19 00:46:55) |
||
( p=strrchr(buff,'\n') ) ? *p='\0': 0; 3項演算子。「式 ? 真の場合 : 偽の場合」 今回のケースでは、下記と同等。 # というか、私だったら、普通にif文で書きますが。 p = strrchr(buff, '\n'); if( p != NULL ){ *p = '\0'; } for( i=0; i<size && *s && !(!iFlag && *s==','); i++,s++ ) 「*s && !(!iFlag && *s==',')」の部分かな? これは、「A かつ B」は「Aでない または Bでない」と同等である事から、 「*s != '\0' && (iFlag == 0 || *s != ',')」と同等。 return s+(*s==','); これは、下記と同等。 # C言語では、式の評価結果が真の場合は 1、偽の場合は 0 となる。 if(*s == ','){ return s + 1; }else{ return s; } コードの読み方は以上。その意味付けはご自分で。 |
No.16241![]() |
Re:Reできました。少し良い方法はないでようか。 投稿者---ごんべ(2004/08/19 10:53:58) |
||
>for( i=0; i<size && *s && !(!iFlag && *s==','); i++,s++ ) >「*s && !(!iFlag && *s==',')」の部分かな? >これは、「A かつ B」は「Aでない または Bでない」と同等である事から、 >「*s != '\0' && (iFlag == 0 || *s != ',')」と同等。 全体のソースを見ていないのでなんともいえませんが、 「*s != '\0' && (iFlag == 0 || *s != ',')」 は 「*s != '\0' && (iFlag != 0 || *s != ',')」 ではないでしょうか。 |
No.16291![]() |
Re:Reできました。少し良い方法はないでようか。 投稿者---RAPT(2004/08/21 22:40:34) |
||
>>for( i=0; i<size && *s && !(!iFlag && *s==','); i++,s++ ) >>「*s && !(!iFlag && *s==',')」の部分かな? >>これは、「A かつ B」は「Aでない または Bでない」と同等である事から、 訂正。 「(A かつ B)でない」は「Aでない または Bでない」と同等 …★ ですね。書きミスでした。 >全体のソースを見ていないのでなんともいえませんが、 >「*s != '\0' && (iFlag == 0 || *s != ',')」 >は >「*s != '\0' && (iFlag != 0 || *s != ',')」 >ではないでしょうか。 そのとおりです。フォローありがとうございます。 お詫びに、数学の証明のような書き方で繙いてみました。 >>「*s && !(!iFlag && *s==',')」 → (*s != 0) && !(!(iFlag != 0) && (*s == ',')) → (*s != 0) && !( (iFlag == 0) && (*s == ',')) ここで、「!( (iFlag == 0) && (*s == ','))」について解くと、 A…(iFlag == 0) B…(*s == ',') とすると、 !( (iFlag == 0) && (*s==',') ) は、 !( A かつ B )となり、★より、 ( !A または !B ) ( !(iFlag == 0) || !(*s == ',') ) ( (iFlag != 0) || (*s != ',') ) となる。従って、 → (*s != 0) && ((iFlag != 0) || (*s != ',')) となる。 |
No.16271![]() |
文字列中にダブルクォーテーションが存在すると 投稿者---よすみ(2004/08/21 09:42:59) |
||
ダブルクォーテーション「"」が文字列中に文字として存在している 場合だと、載せていただいたソースでは上手くいきません。 どうすればよいですか。 /*-------------------------------------------------------*/ char *setvalue(s, t, size) char *s ; char *t ; int size ; { char *e ; int flg = 0 ; e = t + size - 1 ; while (t < e && *s != '\0' && *s != '\n') { if (*s == '\"' && flg == 0) { flg = 1 ; *s++ ; continue ; } if (*s == '\"' && *(s+1) == ',' && *(s+2) == '\"') { break ; } if (*s == '\"' && *(s+1) == '\n') { break ; } *t++ = *s++ ; } *t = '\0' ; return s ; } /*-------------------------------------------------------*/ char *setvalue( char *s, char *t, int size ) { /* ----- 内部変数定義 ----- */ char iFlag = 0; int i; for( i=0; i<size && *s && !(!iFlag && *s==','); i++,s++ ) { switch( *s ) /* 文字判定 */ { case '\"': /* ----- フラグを反転 ----- */ iFlag = !iFlag; break; case ',': if( iFlag ) /* 「"」で括られた文字列外の「,」か? */ { /* ----- 文字をコピー ----- */ *t++ = *s; } break; case '\\': /* ----- 次の文字へ ----- */ s++; default: /* ----- 文字をコピー ----- */ *t++ = *s; break; } } /* ----- ヌルターミネート ----- */ *t = '\0'; return s+(*s==',');} |
No.16274![]() |
Re:文字列中にダブルクォーテーションが存在すると 投稿者---ぽこ(2004/08/21 12:12:50) |
||
>ダブルクォーテーション「"」が文字列中に文字として存在している >場合だと、載せていただいたソースでは上手くいきません。 >どうすればよいですか。 ダブルクォーテーションはデータを囲むメタ文字と見なしていますから、 上手く行かないのは当然です。 (文字としてのダブルコーテーションという概念が存在しないため。) 上手く行かせるには文字としてのダブルコーテーションと、 メタ文字としてのダブルコーテーションを区別するためのルールを 定める必要があります。 |
No.16275![]() |
Re:文字列中にダブルクォーテーションが存在すると 投稿者---Sciggepy(2004/08/21 12:30:57) |
||
>ダブルクォーテーション「"」が文字列中に文字として存在している >場合だと、載せていただいたソースでは上手くいきません。 >どうすればよいですか。 「\"」と書いてダブルクォーテーションを表すことが多いようです。 |
No.16282![]() |
ありがとうございます。 投稿者---よすみ(2004/08/21 15:13:41) |
||
ありがとうございます。 CSVデータのルール、出力方法で対応をします。 |
No.16305![]() |
Re:文字列中にダブルクォーテーションが存在すると 投稿者---ごんべ(2004/08/23 00:18:46) |
||
「\"」とした場合です。 char *setvalue( char *s, char *t, int size ) { /* ----- 内部変数定義 ----- */ char iFlag = 0; int i; for( i=0; i<size && *s != '\0' && (iFlag != 0 || *s != ','); i++,s++ ) { switch( *s ) /* 文字判定 */ { case '\"': if( iFlag == 1 && *(s-1) == '\\' ) /* 「"」で括られた文字列の「"」か? */ { *t++ = *s; } /* ----- フラグを反転 ----- */ else { iFlag = !iFlag; } break; case ',': // if( iFlag ) /* 「"」で括られた文字列外の「,」か? */ if( iFlag == 1 ) /* 「"」で括られた文字列外の「,」か? */ { /* ----- 文字をコピー ----- */ *t++ = *s; } break; case '\\': /* ----- 次の文字へ ----- */ s++; default: /* ----- 文字をコピー ----- */ *t++ = *s; break; } } /* ----- ヌルターミネート ----- */ *t = '\0'; if (*s == ',') { return s + 1; } else { return s; } } |
No.16306![]() |
Re:文字列中にダブルクォーテーションが存在すると 投稿者---nop(2004/08/23 10:44:41) |
||
>「\"」とした場合です。 え〜っと… いちお、私が書いたソースは、 「\」文字によるエスケープに対応したものなので、 修正は必要ないはずですが…?(^^;)ゞ # いちお、そこの動作確認も簡単ながら、 # さらっとやっているのですが…(^^; |
No.16322![]() |
Re:文字列中にダブルクォーテーションが存在すると 投稿者---ごんべ(2004/08/24 00:15:46) |
||
># いちお、そこの動作確認も簡単ながら、 ># さらっとやっているのですが…(^^; すみません。 一度、動かしたとき、上手く行かなかったもので。 環境がわるいのなか。 データかな。 |
No.16316![]() |
Re:文字列中にダブルクォーテーションが存在すると 投稿者---かずま(2004/08/23 18:46:06) |
||
> ダブルクォーテーション「"」が文字列中に文字として存在している > 場合だと、載せていただいたソースでは上手くいきません。 > どうすればよいですか。 CSV の仕様は、 ・一行の中のデータは ,(カンマ)で区切る。行の終りは改行。 ・データがカンマやダブルクォートを含む場合は "(ダブルクォート)で囲む。 ・データの中のダブルクォートはそれをダブルクォート2個("")で置き換える。 実際、Excel で、ab"cd を CSV で保存すると、"ab""cd" になります。 #include <stdio.h> char * setvalue(char *p, char *t, int size) { if (*p == '"') while (*++p && *p != '\n' && (*p != '"' || *++p == '"')) { if (--size > 0) *t++ = *p; } else for (; *p && *p != ',' && *p != '\n'; p++) if (--size > 0) *t++ = *p; *t = '\0'; return *p ? p+1 : p; } int main(void) { char buf[256], field[256], *p; while (fgets(buf, sizeof buf, stdin)) { for (p = buf; *p; ) { p = setvalue(p, field, sizeof field); printf(":%s", field); } puts(":"); } return 0; } |
No.16320![]() |
Re:文字列中にダブルクォーテーションが存在すると 投稿者---かずま(2004/08/23 21:17:14) |
||
" で始まるデータは " で終わらなければならないのですが、万一、"ab"cde のようなデータがあった場合、ab と de の 2つのデータがあるかのような 結果を出すのは変なので、次のように修正します。 char * setvalue(char *p, char *t, int size) { if (*p == '"') while (*++p && *p != '\n' && (*p != '"' || *++p == '"')) if (--size > 0) *t++ = *p; for (; *p && *p != ',' && *p != '\n'; p++) if (--size > 0) *t++ = *p; *t = '\0'; return *p ? p+1 : p; } |
No.16321![]() |
Re:文字列中にダブルクォーテーションが存在すると 投稿者---よすみ(2004/08/23 21:51:22) |
||
ありがとうございす。 データの中のダブルクォートは、\印をつけて文字とする場合は、 ("abc\"def,ghi"のような場合は、)サンプルとして載せて頂いた、 ダブルクォートの個所を\印に置き換えばよいのですか。 |
No.16323![]() |
Re:文字列中にダブルクォーテーションが存在すると 投稿者---かずま(2004/08/24 03:44:55) |
||
> データの中のダブルクォートは、\印をつけて文字とする場合は、 > ("abc\"def,ghi"のような場合は、)サンプルとして載せて頂いた、 > ダブルクォートの個所を\印に置き換えばよいのですか。 いいえ。\" という記法は CSV にはありません。 "abc\"def,ghi" というデータを含む CSVファイルを Excel でオープンすると、 abc\def と ghi" という 2つのデータと解釈されます。 abc"def,ghi という 1つのデータとして解釈して欲しいなら、 "abc""def,ghi" と書かないといけません。それが CSV のルールです。 CSVファイルを読み取るプログラムは、Kernighan & Pike の The Practice of Programming (邦訳「プログラミング作法」) の第4章にあります。 CSV の仕様をきちんと決めた規格書の存在は知りませんが、Web を探せば CSV を解析するプログラムが見つかります。例えば、 http://www.nsknet.or.jp/~kamichan/prog/CSV_pm.html C ではありませんが。 |
No.16324![]() |
ありがとうございます。 投稿者---よすみ(2004/08/24 10:25:09) |
||
かずまさん、nopさん、ごんべさん ありがとうございます。 意見を参考にどうすればベストか、データの内容、ソース等 を考えてみます。 |
No.16325![]() |
Re:ありがとうございます。 投稿者---Sciggepy(2004/08/24 15:07:49) |
||
「CSVの仕様」は見つからなかったのですが、確かにExcelでは'"'を'""'で表していました。 #include <stdio.h> int main(void) { char *src="54,\"we,e\",\"\"\"ese\"\"\"",*p,buf[8]; int q=0,i=0; printf("src: %s\n",src); for(;;src++) { if(*src=='\0') { buf[i]='\0'; printf("%s\n",buf); break; } if(q) { if(*src=='\"') { if(*(src+1)=='\"') { if(i<7) buf[i++]=*(src++); continue; } else q=0; } else if(i<7) buf[i++]=*src; } else { if(*src=='\"') q=1; else if(*src==',') { buf[i]='\0'; printf("%s\n",buf); i=0; } else if(i<7) buf[i++]=*src; } } return 0; } |
No.16326![]() |
Re:ありがとうございます。 投稿者---nop(2004/08/24 15:36:28) |
||
>「CSVの仕様」は見つからなかったのですが、確かにExcelでは'"'を'""'で表していました。 こんなの見つけたけど、どう? File Format |
No.16328![]() |
Re:ありがとうございます。 投稿者---Sciggepy(2004/08/24 17:36:37) |
||
了解しました。 しかし、結局のところ、「規格化された仕様」というのはないようですね。 |