SAK 図書館
VB テクニック編12 - CSV テキストファイル形式仕様、ハンドリングサンプル
■SAK 関数利用規程
・テクニック編で紹介する関数は、私こと Y.SAK の開発関数である。
・著作権明示部分の改編は認めない。
・個人、企業がこれらの関数を使用したり、一部を使用して新たなシステムや
プログラムを開発することは自由です。
・但し、これらの関数を一部でも使用しているソフトウェアをシェアウェア、
その他有償プロダクトとして配布・販売するには、私の許可が必要です。
(無償のフリーソフトウェアなら、自由に配布しても良い。)
・これらの関数を使用して発生した、いかなる形での損害も私こと Y.SAK は
賠償しません。
■CSV テキストファイル形式仕様(CSV 規約)
** 説明中の『』記号は、わかりやすくするために使用しているだけで、実際
のデータには存在しない。
【基本仕様】
・レコード区切りコード: 0x0D, 0x0A (テキスト改行コード CR/LF)
・データ項目区切り記号: カンマ『,』
・データ項目引用句記号: ダブルコーテーション『"』
《例》
012,abcdef,-1,1.12,"abcdef","abc,def"
101,ab,100,-10.2567,"abcdefhij","abc,def,hij"
101,ab,100,-10.2567,abcdefhij,"abc,def,hij"
"101","ab","100","-10.2567","abcdefhij","abc,def,hij"
"101" , "ab" , "100" , "-10.2567","abcdefhij","abc,def,hij"
【区切り記号】
・区切り記号には、カンマ区切りの他に
タブ区切り(TAB 区切り) - タブ文字、タブコードは、chr(9)
空白区切り(スペース区切り)
もある。
尚、VB6 は、カンマ区切りについては、標準 I/O で読める。
但し、なんと全角のカンマも区切りとして認識するようなので要注意である。
昔の Basic は、カンマ、タブ、空白を区切り記号として標準 I/O で読めた
が、最近の VB はダメである。
タブ区切り、空白区切りについては、下段の GetStrItem のカンマ判定箇所
を修正して利用すると良い。
GetStrItem については、次の資料に「無敵版」として、詳しい使用方法が
ある。(無敵版は、全角カンマがあっても誤動作しない。)
VB テクニック編29 - CSV ファイル入出力 簡単版、汎用版、無敵版
尚、区切り文字判定「","」をタブ区切り、空白区切り用に修正すること。
【引用句記号】
・引用句記号には、ダブルコーテーション『"』の他に
シングルコーテーション『'』
もある。
尚、VB6 が標準 I/O で読めるのは、ダブルコーテーション『"』だけである。
但し、全角のダブルコーテーションも引用句として認識するようなので要注
意である。
【補足仕様】
・データ項目中に文字データとしてのカンマ『,』が存在するときは、必ず、
そのデータ項目を引用句『"』で囲まなければならない。
これ以外のデータ項目は、引用句『"』で囲まれていても、囲まれていなく
てもよい。
《例》
○『abcdef』
○『"abcdef"』
×『abc,def』
○『"abc,def"』
・データ項目中に文字データとしてのダブルコーテーション『"』が存在して
はならない。(使用禁止)
《例》
×『abc"def』
×『"abc"def"』
・区切り記号『,』の前後に空白があってもデータの一部と見なさない。
引用句『"』中の空白は、データの一部とみなす。
《例》
『 ab , c 』 →『ab』,『c』
『" ab "," c "』 →『 ab 』,『 c 』
『 " ab " , " c " 』→『 ab 』,『 c 』
・長さ 0 のデータ項目も存在する。
《例》
『,,,』→ 長さ 0 のデータ項目が 4 個あると言う意味。
・数値項目にカンマ編集を使用してはならない。
サインは、マイナスのみデータの先頭につける。
小数部は、ピリオド『.』で区切る。
《例》
○『123456』
○『-123456』
○『-1234.56』
○『-1234.5678』
○『001234.5678』
○『-001234.5678』
○『-001234.567800』
×『123,456』
○『"123456"』
○『"-123456"』
○『"-1234.56"』
○『"-1234.5678"』
○『"001234.5678"』
○『"-001234.5678"』
○『"-001234.567800"』
×『"123,456"』
・ファイルの先頭行に項目見出しレコードが存在することがある。
見出しレコードの仕様もデータレコードと同一である。
実際のプログラムで見出しレコードをどう処理するかは、各ファイルの仕様
による。ファイル中のデータのみで、先頭行が見出しなのか、実データなの
かは判定できない。
従って、ファイルをやり取りする前に、見出しが含まれているか、いないか
仕様確認しておく必要がある。
《例》
『データ1,データ2,データ3』
『012,abcde,aaaa』
■Excel CSV 形式仕様
** Excel の CSV は、特殊な部分が目立つ。Excel での CSV 読み込み、CSV
出力は、上記の一般的な CSV 形式と異なる。
プログラムで、Excel 出力の CSV を扱う際には、個別プログラミングに
なることが多い。
【相違点】
・Excel で CSV を読み込むと、数値と認識できるものは、すべて数値に変換
される。従って、コード項目などのゼロ埋めが飛んでしまう。
これが致命的な結果を生むのは、可変長の先方形式データなどである。
先頭のゼロが飛んでしまうため、オリジナルのコードに復元のしようがない。
得意先コードのように 4 桁固定のものは、『0000』フォーマットで復元で
きる。また、日付項目などもおかしなデータに変換されることがある。
《例》
『"0010"』→『10』
・Excel で CSV を読み込むと、引用句『"』で囲まれていなくても区切り記号
『,』の前後の空白をデータの一部とみなしてしまう。
《例》
『 ab , c 』 →『 ab 』,『 c 』
・Excel で CSV 出力すると、表示イメージのままレコード出力される。
従って、数値のカンマ編集をしていると、そのまま編集イメージで出力され
る結果となる。
・Excel で CSV 出力すると、左右にデータが一切ない列が存在すると、勝手
に列が抹消されてしまう。
言語プログラムで意味のあるダミー項目が抹消されるので注意。
これを防ぐには、見出しレコードを全項目指定するしかない。
《例》
『,a,b,,』 →『a,b』(3 項目抹消される)
《例》
『データ1,データ2,データ3,データ4,データ5』
『,a,b,,』 →『,a,b,,』(見出しがあれば抹消されない)
【補足】
・現実問題として、Excel を介して、CSV をやり取りすると、プログラム側で
の対処がたいへんである。私は、これら問題を解決するために、Excel と
CSV との相互変換プログラムを開発した。
この変換プログラムは、現在のところ日本語版のみであるが、Excel の xls
と標準的な csv の形式変換を確実に行う。
■標準 CSV データ項目取得関数サンプルコード
/*
========================================================================
文字列取得 (C 言語 win16 版) テストしていないので要注意!!
========================================================================
*/
LPSTR GetStrItem(LPSTR rec, int init, LPSTR item)
{
static LPSTR start;
LPSTR str;
LPSTR rtrim;
if (init != STR_NEXT) start = rec;
while (*start != '\0' && *start == ' ') start = AnsiNext(start);
if (*start == '\0') return(NULL);
if (*start == ',') {
start++;
return(lstrcpy(item, ""));
}
if (*start == '"') {
str = ++start;
while (*start != '\0' && *start != '"') start = AnsiNext(start);
lstrcpy(item, str);
if (*start == '\0') return(item);
*(item + (start - str)) = '\0';
start++;
while (*start != '\0' && *start != ',') start = AnsiNext(start);
start++;
}
else {
str = start;
while (*start != '\0' && *start != ',') start = AnsiNext(start);
rtrim = start++;
rtrim = AnsiPrev(rec, rtrim);
while (rtrim != str && *rtrim == ' ') rtrim = AnsiPrev(rec, rtrim);
if (rtrim == str && *rtrim == ' ') return(lstrcpy(item, ""));
lstrcpy(item, str);
*(item + (++rtrim - str)) = '\0';
}
return(item);
}
'=======================================================================
' 文字列取得 (VB6 win32 版) テスト済み
'=======================================================================
public function GetStrItem(rec as string, init as boolean) as string
static start as long
static rln as long
dim str as long
'** 【補足説明】
'** ・VB6 で、標準 CSV を読むには、標準ファイル I/O で処理可能である。
'** ・本関数は、きめ細かなリードをするサンプルにすぎない。
'** 初期化
if init <> STR_NEXT then
start = 1
rln = len(rec)
end if
'** 左辺空白スキップ
for start = start to rln
if mid(rec, start, 1) <> " " then exit for
next
'** データ項目なし
if start > rln + 1 then
GetStrItem = chr(0)
exit function
end if
'** 空項目
if start > rln then
start = start + 1
GetStrItem = ""
exit function
end if
if mid(rec, start, 1) = "," then
start = start + 1
GetStrItem = ""
exit function
end if
'** 引用句 ( chr(&H22) = 『"』)
if mid(rec, start, 1) = chr(&H22) then
start = start + 1
str = start
for start = start to rln
if mid(rec, start, 1) = chr(&H22) then exit for
next
if start > rln then
GetStrItem = mid(rec, str)
else
GetStrItem = mid(rec, str, start - str)
start = start + 1
for start = start to rln
if mid(rec, start, 1) = "," then exit for
next
end if
start = start + 1
exit function
end if
'** データ項目
str = start
for start = start to rln
if mid(rec, start, 1) = "," then exit for
next
if start > rln then
GetStrItem = rtrim(mid(rec, str))
else
GetStrItem = rtrim(mid(rec, str, start - str))
end if
start = start + 1
end function
'=======================================================================
' 項目取得テスト
'=======================================================================
private sub command1_click()
const FILE_NAME = "filename.csv"
const ITEM_MAX = 255
dim fno as long
dim rec as string
dim s as string
dim item(ITEM_MAX) as string
dim itemmax as long
dim i as long
'** ファイルオープン
fno = freefile
open FILE_NAME for input as fno len = 32000
'** レコードループ開始
do until eof(fno)
'** 1 レコードリード
line input #fno, rec
itemmax = -1
s = GetStrItem(rec, STR_INIT)
do until s = chr(0)
itemmax = itemmax + 1
if itemmax > ITEM_MAX then
close fno
msgbox "項目バッファオーバー"
exit sub
end if
item(itemmax) = s
s = GetStrItem(rec, STR_NEXT)
loop
'** 項目バッファ処理
for i = 0 to itemmax
if msgbox("item(" & i & ") = " & item(i), vbOKCancel) = vbCancel then
close fno
exit sub
end if
next
'** レコードループ終了
loop
'** ファイルクローズ
close fno
end sub
■VB テクニック編資料
■VB 入門編資料
■VB 基礎編資料
■VB ビジュアル編資料