SAK 図書館

SAK Streets メインストリート 図書館・カフェ 一覧 SAK Streets 検索
| VB | SQL | Perl | PHP | ASP | JSP | C | PC | HP | FAQ | フリーソフト |

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 ビジュアル編資料
| VB | SQL | Perl | PHP | ASP | JSP | C | PC | HP | FAQ | フリーソフト |

SAK Streets メインストリート 図書館・カフェ 一覧 SAK Streets 検索



Copyright (C) 1999-2017 Y.SAK