Takenoff Labs » Notes/Domino » Lotus Script » CSVの1行を取得する方法(項目内改行対応版)

[Notes/Domino] CSVの1行を取得する方法(項目内改行対応版)

(2012/03/25 コードにバグがあったので訂正しました(_ _;;; )

CSVの規則によると、引用符(ダブルクォーテーション)内では、改行を入れていいことになっています。が、Line Input では改行までを1行として読み込むので、これに対応しようと思うとちょっと面倒なことになります。そんな面倒を解消するため、手軽に扱える関数を作ってみました。

説明 CSVの1行を取得する(項目内改行対応版)
構文 CSVLineInput(Byval intFno As Integer, Byval strDelim As String, Byval strQuote As String) As String
引数 [in,---]intFno ... ファイル番号
[in,---]strDelim ... デリミタ(1文字のみ)
[in,---]strQuote ... 引用符(1文字のみ)
戻り値 CSVの1行分の文字列
  • CSVのルールに反したデータは想定していません。より厳密にやるなら、エラーチェックなどを入れてください。
  • 下記コードでは、1行分の文字列を取得するだけです。データを分割するには、こちらのコードなどを使用してください。
  • 一応業務で2年くらい使用して、今のところ不具合は発生しておりませんが、(2012/03/25追記 すみません、早速バグ発見しました(TヘT 会社のDB直さないと(TヘT )なんかおかしいところとかありましたらご指摘くださいませ(_ _)

ソースコード

Function CSVLineInput(Byval intFno As Integer, Byval strDelim As String, _
Byval strQuote As String) As String
    Dim strRow As String
    Dim strTmp As String
    
    If intFno = 0 Or Eof(intFno) Then
        CSVLineInput = ""
        Exit Function
    End If
    
    strRow = ""
    Line Input #intFno, strTmp
    
    strRow = strTmp
    strTmp = Replace(strTmp, strQuote & strQuote, "") '「""」は判定に邪魔なので消しておく
    
    If Right(Strleftback(strDelim & strTmp, strQuote), 1) = strDelim And _
    ((Len(strTmp) - Len(Replace(strTmp, strQuote, ""))) Mod 2) = 1 Then
        
        Do Until Eof(intFno)
            
            Line Input #intFno, strTmp
            
            strRow = strRow & Chr(13) & Chr(10) & strTmp
            strTmp = Replace(strTmp, strQuote & strQuote, "")
            
            If Instr(strTmp, strQuote) > 0 And _
            Not(Right(Strleftback(strDelim & strTmp, strQuote), 1) = strDelim And _
            ((Len(strTmp) - Len(Replace(strTmp, strQuote, ""))) Mod 2) = 0) Then
                Exit Do
            End If
        Loop
        
    End If
    
    CSVLineInput = strRow
End Function

使い方

Dim strRet As String
Dim intFno As Integer

intFno = Freefile()
Open "C:\test.csv" For Input As intFno

Do Until Eof(intFno)
    strRet = CSVLineInput(intFno, {,}, {"})
    'ToDo: ここに strRet を分割する処理を書きます。
Loop

Close #intFno

ちょっと解説

以下の条件がちょっとわかりにくいかもしれないので、解説しときます。

If Right(Strleftback(strDelim & strTmp, strQuote), 1) = strDelim And _
Right(strTmp, 1) <> strQuote Then
((Len(strTmp) - Len(Replace(strTmp, strQuote, ""))) Mod 2) = 1 Then

ここでは、引用符が開いているかどうかを判定しています。まずミソは、このコードの直前で「""」のデータ(エスケープされた引用符)を消していること(もちろん、変更後の文字列は判定用にしか使いません)。このようにすると、引用符が開いているとしたら、

・・・・,"・・・

か、

"・・・

のパターンしかありえません。すると、「右から検索して、引用符のすぐ左がデリミタ or 先頭」かつ「右の1文字が引用符でない」という条件となります。この条件を表したものが、上記のIF文になります。(2012/03/25追記 「右の1文字が引用符でない」という条件にバグがありました(_ _;;; 先頭が改行のデータがあった場合、右の1文字は引用符になってしまいます(´д`)。仕方ないので、「引用符が奇数個の場合」という条件に変更しました。)

「strDelim & strTmp」と先頭にデリミタを付けて Strleftback しているのは、番兵です。このようにしておくと、引用符のすぐ左が先頭である場合もデリミタが返ることになるので、条件を1つにまとめることができます。

(2012/03/25追記 CSVの形式が正しければ、引用符の個数が奇数かどうかを判断するだけでよいかもしれません。この場合、以下のようにもうちょっとシンプルなロジックになります(デリミタの引数すら要らない)。ただ、これは個人的には若干危険なような気もします。)

Function CSVLineInput(Byval intFno As Integer, Byval strQuote As String) As String
    Dim strRow As String
    Dim strTmp As String
    
    If intFno = 0 Or Eof(intFno) Then
        CSVLineInput = ""
        Exit Function
    End If
    
    strRow = ""
    Line Input #intFno, strTmp
    
    strRow = strTmp
    strTmp = Replace(strTmp, strQuote & strQuote, "") '「""」は判定に邪魔なので消しておく
    
    If ((Len(strTmp) - Len(Replace(strTmp, strQuote, ""))) Mod 2) = 1 Then
        
        Do Until Eof(intFno)
            
            Line Input #intFno, strTmp
            
            strRow = strRow & Chr(13) & Chr(10) & strTmp
            strTmp = Replace(strTmp, strQuote & strQuote, "")
            
            If ((Len(strTmp) - Len(Replace(strTmp, strQuote, ""))) Mod 2) = 1 Then
                Exit Do
            End If
        Loop
        
    End If
    
    CSVLineInput = strRow
End Function
1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...

トラックバックURL :

Navigation

前の記事(カテゴリ内):

次の記事(カテゴリ内):

前の記事(日付順):

次の記事(日付順):

トラックバック

トラックバックはありません

コメント

コメントはありません

※コメントは承認制となっております。管理者が承認するまで表示されません。申し訳ありませんが、投稿が表示されるまでしばらくお待ちください。





(以下のタグが使えます)
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

For spam filtering purposes, please copy the number 5784 to the field below: