views:

710

answers:

4

I have a need to read a CSV file, and the only language I can use is VBscript.

I'm currently just opening the file and splitting on commas, and it's working OK because there aren't any quoted commas in fields. But I'm aware this is an incredibly fragile solution.

So, is there such a thing as a VBscript module I can use? Somewhere to get a tried-and-tested regular expression that would only split on commas not in quotes?

Any suggestions gratefully received.

A: 

You can try creating an Excel ODBC Data Source to CSV (Called DSN I think. Its in Control Panel -> Administrative Tools -> ODBC Data Sources. Then on, you can query it using SQL.

I am still unsure if you can get what you want. I mean inserting a string with commas in it as a value for a particular cell.

daanish.rumani
A: 

A regexp:

'Credits go to http://www.codeguru.com/cpp/cpp/algorithms/strings/article.php/c8153/
r.Pattern = ",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))"

It will find all commas that are not inside quotes.

Alternatively, you can use this function which I just adapted for vbs.

call test


Function ParseCSV(StringToParse, Quotes)
  Dim i, r(), QuotedItemStart, prevpos

  ReDim r(0)
  prevpos = 1

  For i = 1 To Len(StringToParse)
    If Mid(StringToParse, i, 1) = "," Then
      If QuotedItemStart = 0 Then
        r(UBound(r)) = Trim(Mid(StringToParse, prevpos, i - prevpos))
        ReDim Preserve r(UBound(r) + 1)
        prevpos = i + 1
      End If
    Else
      If InStr(1, Quotes, Mid(StringToParse, i, 1)) Then
        If QuotedItemStart Then
          r(UBound(r)) = Trim(Mid(StringToParse, QuotedItemStart, i - QuotedItemStart))
          ReDim Preserve r(UBound(r) + 1)
          QuotedItemStart = 0
          prevpos = i + 2
          i = i + 1
        Else
          QuotedItemStart = i + 1
        End If
      End If
    End If
  Next

  If prevpos < Len(StringToParse) Then r(UBound(r)) = Trim(Mid(StringToParse, prevpos))
  ParseCSV = r
End Function


Sub Test()
  Dim i, s

  s = ParseCSV("""This is, some text!"",25,""Holy holes!"", 286", """")

  For i = LBound(s) To UBound(s)
    msgbox s(i)
  Next

  msgbox "Items: " & CStr(UBound(s) - LBound(s) + 1)
End Sub
GSerg
+7  A: 

VBScript does not have a module system comparable to Perl. However you can open CSV files with ADO and access them like a database table. The code would go something like this:

(The funny comments are solely to fix SO's broken VB syntax highlighting)

Dim conn    ''// As ADODB.Connection
Dim rs      ''// As ADODB.RecordSet
Dim connStr ''// As String
Dim dataDir ''// As String

dataDir = "C:\"                         '"
connStr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & dataDir & ";Extended Properties=""text"""

Set conn = CreateObject("ADODB.Connection")
conn.Open(connStr)
Set rs = conn.Execute("SELECT * FROM [data.txt]")

''// do something with the recordset
WScript.Echo rs.Fields.Count & " columns found."
WScript.Echo "---"

WScript.Echo rs.Fields("Col1Name").Value
If Not rs.EOF Then
  rs.MoveNext
  WScript.Echo rs.Fields("Col3Name").Value
End If

''// explicitly closing stuff is somewhat optional
''// in this script, but consider it a good habit
rs.Close
conn.Close

Set rs = Nothing
Set conn = Nothing

Creating a schema.ini file that exactly describes your input is optimal. If you don't, you force the text driver to guess, and all bets are off if it guesses the wrong thing. The schema.ini must reside in the same directory where your data is.

Mine looked like this:

[data.txt]
Format=Delimited(;)
DecimalSymbol=.
ColNameHeader=True
MaxScanRows=0
Col1=Col1Name Long
Col2=Col2Name Long
Col3=Col3Name Text
Col4=Col4Name Text

and with this data.txt:

a;b;c;d
1;2;"foo bar";"yadayada"
1;2;"sample data";"blah"

I get this output:

C:\>cscript -nologo data.vbs
4 columns found.
---
1
sample data

C:\>

Worth a read in this regard: Much ADO About Text Files off the MSDN.

Tomalak
I'm trying to run the script as shown and it's complaining that there's no Wscript object. What am I missing?
AmbroseChapel
You are not trying to run this from within a VB environment, are you? This is VBScript, not VB.
Tomalak
A: 

To answer the other half of your question, I have a vague recollection that you can use Windows Script Host spread across several WSF files. I have never done it myself, link to MSDN. Not pure VBS, but it should work in 'just' windows, if that was the real constraint.

More links:

Benjol