【掲示板ご利用上の注意】

 ※題名は具体的に!
 ※学校の課題の丸投げ禁止!
 ※ソースの添付は「HTML変換ツール」で字下げ!
 ※返信の引用は最小限に!
 ※環境(OSとコンパイラ)や症状は具体的に詳しく!
 ※マルチポスト(多重投稿)は慎んで!

 詳しくはこちら



 本当はこんなに大きく書きたくはないのですが、なかなか守っていただけなくて…。
 守ってくださいね。お願いします。(by管理人)

C言語ソース⇒HTML形式ツール   掲示板2こちら


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

No.20991

CSVデータの読み込み
投稿者---こーたろう(2005/05/11 01:49:09)


CSVファイルから読み込みを行っています。
CCSVデータは、カンマで区切られており、ダブルクォーテションで囲まれています。
カンマ毎にダブルクォーテションを取り除いた部分の値を取得しようとしているの
ですが、2バイト文字、1バイト文字が存在、おもったような処理ができません。
2バイト文字が存在しているので、最初に2バイトもじかそうでないかをチェック
しているのですが、2バイト文字の間に「"」とか「¥”」で存在すると取得結果
がおかしくなってしまいます。
また、2バイト文字の2バイト目が「\」の0x5C"であったりするとおかしくなって
しまいます。
どうすればうまくいきますか。

"000001","山田","太郎","yamada","taro"
"000002","山"田","花"子","yamada","hanako"
"000003","鈴\"木","一"\郎","SUZUKI","ICHRO"
"000004","表","次郎","omote","jiro"
"000005","山本","光\"太郎",ヤマモト","コウタロウ"


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

#define ISKANJI(c)       (c >= 0x81 && c <= 0x9F || c >=0xE0 && c <= 0xFC)

char *read_csv_data(char *, char *, int);

typedef struct
{
    char str1[64];
    char str2[64];
    char str3[64];
    char str4[64];
    char str5[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');
        if ( p != NULL ) {
            *p = '\0';
        }

        p = read_csv_data(buff,temp_data.str1,sizeof(temp_data.str1));
        p = read_csv_data(p,temp_data.str2,sizeof(temp_data.str2));
        p = read_csv_data(p,temp_data.str3,sizeof(temp_data.str3));
        p = read_csv_data(p,temp_data.str4,sizeof(temp_data.str4));
        p = read_csv_data(p,temp_data.str5,sizeof(temp_data.str5));
        printf( ":%s:%s:%s:%s:%s:\n",temp_data.str1,temp_data.str2,temp_data.str3,temp_data.str4,temp_data.str5);
    }
    return 0;
}

char *read_csv_data(char *s, char *t, int size)
{
    unsigned char c;
    char  iFlag = 0;
    char  fKANJI = 0;
    int   i;

    for(i=0; i<size && *s != '\0' && (iFlag != 0 || *s != ','); i++,s++)
    {
        if (ISKANJI(c))
        {
            *t++ = c;
            *t++ = *(++s);
            fKANJI = 1;
        }
        else
        {
            switch( *s )
            {
            case '\"':
                if (fKANJI == 0)
                {
                    iFlag = !iFlag;
                }
                else if(iFlag == 1 && *(s-1) == '\\')
                {
                    *t++ = *s;
                }
                else
                {
                    iFlag = !iFlag;
                }
                break;
            case ',':
                if(iFlag == 1)
                {
                    *t++ = *s;
                }
                break;
            case '\\':
                s++;
            default:
                *t++ = *s;
                break;
            }
        }
    }
    *t = '\0';

    if (*s == ',') {
        return s + 1;
    } else {
        return s;
    }
}



この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:CSVデータの読み込み 20992 あかま 2005/05/11 06:36:27
<子記事> Re:CSVデータの読み込み 20993 まきじ 2005/05/11 09:06:05
<子記事> Re:CSVデータの読み込み 20996 REE 2005/05/11 10:18:18
<子記事> Re:CSVデータの読み込み 20997 ぼく 2005/05/11 10:40:52
<子記事> Re:データ取得部分訂正 20999 こーたろう 2005/05/11 10:47:22
<子記事> Re:CSVデータの読み込み 21000 Ban 2005/05/11 11:14:40
<子記事> 皆様、色々とアドバイスありがとうございます。 21017 こーたろう 2005/05/12 15:32:38


No.20992

Re:CSVデータの読み込み
投稿者---あかま(2005/05/11 06:36:27)


いっそ"\",\""を見つけて文字列を区切ってしまえば楽かもしれない。


この投稿にコメントする

削除パスワード

No.21010

Re:CSVデータの読み込み
投稿者---あかま(2005/05/12 00:08:48)


>いっそ"\",\""を見つけて文字列を区切ってしまえば楽かもしれない。
というわけで、文字列が常に""で囲まれてるのを前提で書いてみました。

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

int read_csv_data(char *str,char **p){//分割した文字列を*p[]に格納。分割した件数を返す
    char *c;
    int i=0;
    p[0] = str+1;
    
    c = strstr(str,"\",\"");
    do{
        i++;
        *c = '\0';
        p[i] = c+3;
    }while(c = strstr(c+3,"\",\""));
    p[i][strlen(p[i])-1] = '\0';
    return i+1;
}
int main(){
    char str[] = {"\"000005\",\"山本\",\"光\"太,郎\",\"ヤマモト\",\"コウタロウ\""};
    char *p[10];
    int i,num;
    
    printf("%s\n",str);
    num = read_csv_data(str,p);
    for(i=0;i < num;i++){
        printf("%s\n",p[i]);
    }
    return 0;
}






この投稿にコメントする

削除パスワード

No.20993

Re:CSVデータの読み込み
投稿者---まきじ(2005/05/11 09:06:05)


>カンマ毎にダブルクォーテションを取り除いた部分の値を取得しようとしているのですが、

strtok で , 区切りに読み込んで、strlen() - 1 に \0 を入れて
2 文字目から格納すれば良いと思います。

参考までに
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define N 32

int main(void){

    char src[N] = "\"bar\",\"foo\",\"hoge\"";
    char buf[N];
    char str[N];
    char *token;
    
    token = strtok(src,",");
    while(token != NULL){
        sprintf(buf,"%s",token);
        buf[strlen(buf) - 1 ]= '\0';
        strcpy(str,buf + 1);
        printf("%s\n",str);
        token = strtok(NULL,",");
    }
    
    return EXIT_SUCCESS;
    
}

ダブルクォーテーションで囲む必要があるのでしょうか?


この投稿にコメントする

削除パスワード

No.20994

Re:CSVデータの読み込み
投稿者---Ban(2005/05/11 10:16:37)


> sprintf(buf,"%s",token);

単に strcpy でよい気がしますが、使い分けに理由はあるのでしょうか。


>ダブルクォーテーションで囲む必要があるのでしょうか?

CSV の一般的仕様(?)だと、文字列中に , を含む場合などに
囲む必要があったと思います。
# バックスラッシュなどの文字でエスケープする仕様など、
# 微妙に方言がありますが。

常に囲う必要はないはずですが、囲ってもいいはずで、
(中身の文字を判断しないために)常にそういう出力を
吐いてくるソフトもあります。


この投稿にコメントする

削除パスワード

No.20998

Re:CSVデータの読み込み
投稿者---まきじ(2005/05/11 10:41:07)


>単に strcpy でよい気がしますが、使い分けに理由はあるのでしょうか。

strcpy でよかったですね、、、

>CSV の一般的仕様(?)だと、文字列中に , を含む場合などに
>囲む必要があったと思います。

仕様でしたか、知りませんでした。


この投稿にコメントする

削除パスワード

No.20996

Re:CSVデータの読み込み
投稿者---REE(2005/05/11 10:18:18)


>char *read_csv_data(char *s, char *t, int size)
>{
> unsigned char c;
>(略)
> if (ISKANJI(c))

このcに初期化も代入もされていません。



この投稿にコメントする

削除パスワード

No.20997

Re:CSVデータの読み込み
投稿者---ぼく(2005/05/11 10:40:52)


いつも拝見、参考にさせていただいております。

良い方法かどうかわかりませんが、2バイト文字だった場合、
その次にくる文字をチェックすればいいのではないですか。

if (fKANJI == 0 && *(s+1) == ',') {
iFlag = !iFlag ;
} else if (fKANJI == 0 && *(s+1) != ',') {
*t++ = *s ;
}

「if (fKANJI == 0)」でいいのかな。?

でも、あまり難しくなるような処理になるのであれば、
あかまさんやまさじさんが書かれているような関数を利用して
簡単にしてみてはいかがでしょうか



この投稿にコメントする

削除パスワード

No.20999

Re:データ取得部分訂正
投稿者---こーたろう(2005/05/11 10:47:22)


訂正です。

char *read_csv_data(char *s, char *t, int size)
{
    unsigned char c;
    char  iFlag = 0;
    char  fKANJI = 0;
    int   i;

    for(i=0; i<size && *s != '\0' && (iFlag != 0 || *s != ','); i++,s++)
    {
        c = *s;
        if (ISKANJI(c))
        {
            *t++ = c;
            *t++ = *(++s);
            fKANJI = 1;
        }
        else
        {
            switch( *s )
            {
            case '\"':
                if (fKANJI == 1)
                {
                    iFlag = !iFlag;
                }
                else if(iFlag == 1 && *(s-1) == '\\')
                {
                    *t++ = *s;
                }
                else
                {
                    iFlag = !iFlag;
                }
                break;
            case ',':
                if(iFlag == 1)
                {
                    *t++ = *s;
                }
                break;
            case '\\':
                s++;
            default:
                *t++ = *s;
                break;
            }
        }
    }
    *t = '\0';

    if (*s == ',') {
        return s + 1;
    } else {
        return s;
    }
}





この投稿にコメントする

削除パスワード

No.21002

Re:データ取得部分訂正
投稿者---REE(2005/05/11 11:17:59)


きっと漢字と\"を特別視しすぎています。
肝心なのは、漢字は2バイトまとめて処理することと、\はその次の文字を制御文字と見ないだけです。

    for(i=0; i<size && *s != '\0' && *s != ','; i++,s++)
    {
        c = *s;
        if (ISKANJI(c))
        {
            *t++ = c;
            *t++ = *(++s);
        }
        else if(c == '\\')
        {
            *t++ = *(++s);
        }
        else if(c != '\"')
        {
            *t++ = c;
        }
    }




この投稿にコメントする

削除パスワード

No.21000

Re:CSVデータの読み込み
投稿者---Ban(2005/05/11 11:14:40)


マルチバイト文字列(S-JIS)の lead-trail を意識した処理をするのが
正攻法だと思いますが、環境依存なら _mbs... (VCの場合)とか使うのも一考でしょうか。

# C++ なら wchar_t で UNICODE とか...。


この投稿にコメントする

削除パスワード

No.21003

Re:CSVデータの読み込み
投稿者---こーたろう(2005/05/11 21:09:37)



皆様、色々とアドバイスありがとうございます。


環境を書き忘れましたが、Solaris8です。


この投稿にコメントする

削除パスワード

No.21007

Re:CSVデータの読み込み
投稿者---Ban(2005/05/11 22:11:43)


>環境を書き忘れましたが、Solaris8です。

環境がないので分りませんが、C99 なら C でもワイド文字列はサポートしてるはず。

REE さんのレスを理解すれば、こんなこといらないですけど。
> きっと漢字と\"を特別視しすぎています。
> 肝心なのは、漢字は2バイトまとめて処理することと、
> \はその次の文字を制御文字と見ないだけです


この投稿にコメントする

削除パスワード

No.21017

皆様、色々とアドバイスありがとうございます。
投稿者---こーたろう(2005/05/12 15:32:38)



皆様、色々とアドバイスありがとうございます。



この投稿にコメントする

削除パスワード

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