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 ビジュアル編資料