Обработка VBScript CSV (RFC4180)
Позвольте мне предвосхитить это, сказав, что я сисадмин с небольшим воздействием на разработчика. Если это не вопрос для stackru, пожалуйста, не стесняйтесь перемещать / удалять / указывать меня в правильном направлении.
Я пытаюсь прочитать csv-файл, совместимый с RFC4180, и выбросить каждое значение в массив для дальнейшей обработки по сценарию. Ниже приведена наиболее замысловатая, но совместимая линия CSV, которую я смог придумать, и она работает, но этот сценарий будет ориентирован на клиентов, поэтому я хотел бы, чтобы вы посмотрели и протестировали логику / показали, где Я пропустил вещи.
Я бы тоже хотел указатели лучших практик, если это возможно.
Это выдержка, в основном я читаю в строке из файла CSV и перебираю каждый символ, тестирую и применяю разные вещи, основываясь на моем понимании RFC 4180. chr(34) представляет двойные кавычки ("), это единственный способ Я мог бы найти, чтобы сделать сравнение в VBScript.
If isFirstChar = True And thisChar = chr(34) Then
'Mark it as in quotes, but don't print it, as it's not part of the text meant for the din file
isInQuotes = True
isFirstChar = False
ElseIf thisChar = chr(34) And isInQuotes = True Then
If nextChar = chr(34) Then
'Per RFC4108, "" is an escape sequence
'Print it, jump up the old CharCounter to prevent double handling.
CharCounter = CharCounter +1
FieldData = FieldData & thisChar
isFirstChar = False
ElseIf nextChar = "," Then
'It's the end of the field, we can set the isInQuotes to false so that the next char is handled as end of field
isInQuotes = False
isFirstChar = False
Else
'CSV isn't RFC4180 compliant
WScript.Echo "This CSV isn't RFC4180 compliant, please fix the file or contact <redacted> for assistance."
End If
ElseIf thisChar = "," Then
If isInQuotes = False Then
'End of Field, handle appropriately
FieldCounter = FieldCounter + 1
Redim Preserve FieldArray(FieldCounter)
FieldArray(FieldCounter) = FieldData
FieldData = ""
isFirstChar = True
Else
'In quotes, handle as regular char
FieldData = FieldData & thisChar
isFirstChar = False
End If
Else
'Got all the way here, it's just a regular character! (We hope)
FieldData = FieldData & thisChar
isFirstChar = False
End If
А ниже приведен пример строки из записи CSV:
"Luke ,, Pearson","Luke ""Lucky""","""111 ""Brown Mountain Cres",,"CO""OROY",QLD,4563,,1234567,1,1.11,N,AT FRONT GATE,0712345678,0.0022,2
1 ответ
Я хотел автономное решение, поэтому вот что я придумал. (Я уверен, что есть возможности для улучшения):
Private Function CSVSplit(ByVal csv As String) As String()
' see RFC 4180
If csv = Empty Then
Dim emptyArray(-1 To -1) As String
CSVSplit = emptyArray
Exit Function
End If
Dim i As Long
Dim tokenBuf As Collection: Set tokenBuf = New Collection
Dim tokens As Collection: Set tokens = New Collection
Dim token As String
Dim inQuotes As Boolean
Dim nextChar As String
Dim c_ As Variant
For i = 1 To Len(csv)
Dim c As String: c = Mid(csv, i, 1)
If i = Len(csv) Then
nextChar = Empty
Else
nextChar = Mid(csv, i + 1, 1)
End If
Select Case c
Case ","
If inQuotes Then
Call tokenBuf.Add(c)
Else
token = ""
For Each c_ In tokenBuf
token = token & c_
Next
Call tokens.Add(token)
Set tokenBuf = New Collection
End If
Case """"
If Not inQuotes Then
inQuotes = True
ElseIf nextChar = """" Then
i = i + 1
c = Mid(csv, i, 1)
' quote literal
Call tokenBuf.Add(c)
Else
inQuotes = False
End If
Case Else
Call tokenBuf.Add(c)
End Select
Next
token = ""
For Each c_ In tokenBuf
token = token & c_
Next
Call tokens.Add(token)
Dim result() As String
ReDim result(tokens.count - 1)
For i = 0 To tokens.count - 1
result(i) = tokens(i+1)
Next
CSVSplit = result
End Function
Протестировано со следующим (обратите внимание наWriteLine
function - это просто пользовательская функция, которая выводит на консоль для целей тестирования.:
Dim s As Variant
Dim ss(3) As String
ss(0) = "Panes, Trains, Automobiles"
ss(1) = "Wide, He Said ""Tall, Skinny and round"", ""Narrow, but """"manageable"""""
ss(2) = "Speaker: Cindy, Greeting: """"""Hello, " & vbNewLine & "world"""""", Farewell: Goodbye"
ss(3) = "Lions, Tigers, Bears,"
For Each s in ss
Dim arr() As String: arr = CSVSplit(s)
Dim i As Integer
For i = 0 To UBound(arr)
Call WriteLine("_________________________________________________")
Call WriteLine(arr(i))
Call WriteLine("_________________________________________________")
Call WriteLine(vbNewLine)
Next
Next
Выход :
_________________________________________________
Panes
_________________________________________________
_________________________________________________
Trains
_________________________________________________
_________________________________________________
Automobiles
_________________________________________________
_________________________________________________
Wide
_________________________________________________
_________________________________________________
He Said Tall, Skinny and round
_________________________________________________
_________________________________________________
Narrow, but "manageable"
_________________________________________________
_________________________________________________
Speaker: Cindy
_________________________________________________
_________________________________________________
Greeting: "Hello,
world"
_________________________________________________
_________________________________________________
Farewell: Goodbye
_________________________________________________
_________________________________________________
Lions
_________________________________________________
_________________________________________________
Tigers
_________________________________________________
_________________________________________________
Bears
_________________________________________________
_________________________________________________
_________________________________________________