スポンサーリンク

このドキュメントの内容は、以下の通りです。

はじめに


CSVという言葉を耳にしたことがある方は多いのではないでしょうか?
CSV とは、カンマ セパレータ バリューのことで、それぞれの単語の頭文字からCSVと略して呼ばれています。CSVは、名前の通り、バリュー(値)が、「カンマ」で区切られて格納されたファイルのことを言います。ファイル名の拡張子は .csv です。
学校やオフィスで、CSVファイルを扱ったことがある方がいらっしゃると思います。Windows のパソコンで Office を利用されている方は Excel で、表として扱っているのではないでしょうか?CSVの親戚にTSV(タブセパレータバリュー)というタブで区切った形式のファイルもあります。
CSVやTSVは、表計算ソフトやなんらかのアプリケーションで作成されます。
CSV/TSVは、データのやりとりを受け渡しするために、よく利用されています。
CSV/TSVは、ただのテキストファイルなので、Linux/Unix などのコマンドでも扱いやすいファイルです。

CSVをExcelなどの表計算ソフトで扱う場合もあれば、プログラムで扱う場合もあります。ここでは、C言語で扱う方法について考えてみます。

時代遅れの関数について


scanf の関数は、バッファオーバーランの危険性があるため、以前より問題のある関数とされていました。 そのため、JPCERTのホームページのドキュメントでは、scanf や sscanf のような関数は、時代遅れの関数と言われています。

そして、時代遅れでない関数があります。
sscanf の場合では、 sscanf_sです。ただし、sscanf_s は、マイクロソフトの Visual Stdio (ビジュアルスタジオ)などの限られた環境でしか利用できないようです。

C言語でCSVを読む方法を考える


C言語でCSV(カンマセパレーテッドバリュー)の文字列を読み込む方法について考えます。
ここでは、CSVのファイルではなく、カンマで区切られた文字列を扱います。
CSVのファイルを扱いたい場合には、 fgets でループを回しながら、 sscanf を利用する、とといった方法が考えられます。

C言語には、データを入力するための関数として scanfが古くから利用されています。scanfは、標準入力(stdin) から受け取ります。 fscanfはファイルから入力します。sscanfは、文字列を入力として扱います。

このような文字列をsscanf関数で読み込みます。

abc,def,ghi


この例だと、うまくいきません。

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

#define BUF     16

int
main (int argc, char *argv[])
{
        char    *str = "abc,def,ghi";

        char    buf1[BUF];
        char    buf2[BUF];
        char    buf3[BUF];

        int     r;

        r = sscanf (str, "%s,%s,%s", buf1, buf2, buf3);
        printf ("n=%d, [%s] [%s] [%s]\n", r, buf1, buf2, buf3);

        exit (EXIT_SUCCESS);
}

下記のプログラムの場合、1つしかとれてません。
r = sscanf (str, "%s,%s,%s", buf1, buf2, buf3);

buf1 にしか、値が入っていません。
buf2, buf3 は、未初期化の影響で、メモリに入っていた謎のデータが表示されています。
% ./a.out
n=1, [abc,def,ghi] [] [a(壟真☆(_(]

上記の問題は、フォーマット指定子の %sに 「, 」 (カンマ)が含まれてしまうのが原因です。

これに対して、下記のように修正をしてみます。
r = sscanf (str, "%[^,],%[^,],%[^,]", buf1, buf2, buf3);
なんだか、正規表現っぽくなってきました。

サンプルのプログラムを以下のように書き直しました。
#include <stdio.h>
#include <stdlib.h>

#define BUF     16

int
main (int argc, char *argv[])
{
        char    *str = "abc,def,ghi";

        char    buf1[BUF];
        char    buf2[BUF];
        char    buf3[BUF];

        int     r;

        r = sscanf (str, "%[^,],%[^,],%[^,]", buf1, buf2, buf3);
        printf ("n=%d, [%s] [%s] [%s]\n", r, buf1, buf2, buf3);

        exit (EXIT_SUCCESS);
}

これで、, (カンマ)をセパレータとして、読み込むことができます。
以下の通り、うまくいっていることが確認できました。

% ./a.out
n=3, [abc] [def] [ghi]

ライブラリでCSVをパースできないのか


Python だと簡単に CSV ファイルを扱うようなライブラリがあります。
C言語ではどうでしょうか?

調べてみると libcsv といった、それっぽいライブラリが出てきます。
github に libcsv のソースコードがあり、FreeBSD には libcsv のパッケージがあります。 Ubuntu でも libcsv-dev や libcsv3 といった CSV のパーサーとライターのライブラリがあります。

sscanf でやるにも限界があると思います。sscanf は、標準ライブラリだけでお手軽にできるわけですが、バグのないコードを1から書くのも骨が折れるため、ライブラリでやったほうが良いのではないかと思います。

C言語関連の記事

[2007-12-22-1] FreeBSD C言語のerrnoの実装
[2007-10-01-1] C言語でカレントディレクトリを取得する getcwdの使い方
[2007-09-23-3] C言語の_Boool型とC99とgccとstdbool.hのbool型
[2007-07-17-1] C言語による間違えトップ10
[2007-07-16-1] C言語使いのJavaScript strstr
[2007-07-02-4] Part1 オープンソース/C言語に学ぶ「ソースコードの読み方」
参照しているページ (サイト内): [2007-10-01-1]

スポンサーリンク
スポンサーリンク
 
いつもシェア、ありがとうございます!


もっと情報を探しませんか?

関連記事

最近の記事

人気のページ

スポンサーリンク
 

過去ログ

2020 : 01 02 03 04 05 06 07 08 09 10 11 12
2019 : 01 02 03 04 05 06 07 08 09 10 11 12
2018 : 01 02 03 04 05 06 07 08 09 10 11 12
2017 : 01 02 03 04 05 06 07 08 09 10 11 12
2016 : 01 02 03 04 05 06 07 08 09 10 11 12
2015 : 01 02 03 04 05 06 07 08 09 10 11 12
2014 : 01 02 03 04 05 06 07 08 09 10 11 12
2013 : 01 02 03 04 05 06 07 08 09 10 11 12
2012 : 01 02 03 04 05 06 07 08 09 10 11 12
2011 : 01 02 03 04 05 06 07 08 09 10 11 12
2010 : 01 02 03 04 05 06 07 08 09 10 11 12
2009 : 01 02 03 04 05 06 07 08 09 10 11 12
2008 : 01 02 03 04 05 06 07 08 09 10 11 12
2007 : 01 02 03 04 05 06 07 08 09 10 11 12
2006 : 01 02 03 04 05 06 07 08 09 10 11 12
2005 : 01 02 03 04 05 06 07 08 09 10 11 12
2004 : 01 02 03 04 05 06 07 08 09 10 11 12
2003 : 01 02 03 04 05 06 07 08 09 10 11 12

サイト

Vim入門

C言語入門

C++入門

JavaScript/Node.js入門

Python入門

FreeBSD入門

Ubuntu入門

セキュリティ入門

パソコン自作入門

ブログ

トップ


プライバシーポリシー