我需要一些指导如何解决我正在处理的问题。根本问题是我需要在另一个程序中使用CSV文件。创建CSV文件的源系统不会在导出的任何数据字段中删除CRLF(这意味着某些字段具有嵌入的CRLF)。因此,我收到一个CSV文件,其中包含格式错误的行。我的最终目标是实用程序
在下面的例子中,我正在查看列数。如果正确的计数是18,那么我希望它将该行写入新文件。如果列数不正确,我想从该行中删除CRLF,直到列计数正确。
同样,有两种方法可以解决我所知道的问题:
到目前为止,我对代码的问题是能够将有效行写出到新文件中。目前它写出System.String[]
而不是实际的行。
Public Class Form1
Private Sub btnFixit_Click(sender As Object, e As EventArgs) Handles btnFixit.Click
Dim iBadRowNumber As Integer = vbNull
Dim strFixedFile As System.IO.StreamWriter = My.Computer.FileSystem.OpenTextFileWriter(Me.txtFixedFile.Text, True)
Using MyReader As New Microsoft.VisualBasic.FileIO.TextFieldParser(Me.txtBaselineFileToProcess.Text)
MyReader.TextFieldType = FileIO.FieldType.Delimited
MyReader.SetDelimiters(",")
Dim currentRow As String()
While Not MyReader.EndOfData
Try
currentRow = MyReader.ReadFields()
If currentRow.Count = 18 Then
strFixedFile.WriteLine(currentRow)
Else
' Future code here to fix the line
End If
Catch ex As Microsoft.VisualBasic.FileIO.MalformedLineException
MsgBox("Line " & ex.Message &
"is not valid and will be skipped.")
End Try
End While
End Using
strFixedFile.Close()
End Sub
End Class
以下是2个正确行的示例,中间有一行不正确的行。在此示例中,以Sometown开头的行实际上是前一行的一部分。我还看到一个真正的行可能被分成三个或更多的部分行,就像你在Sometown行中看到的那样。
CustomerId,CustomerName,Status,Type,CustomerNumber,DBA,Address1,Address2,City,State,ZipCode,WebAddr,EMail,SalesCode,ServiceCode,DivisionCode,BranchCode,DepartmentCode 6d0125cd-70cf-4048-9ee1-8d9682e426a5,"Smith,James",Active,Customer,8,,103 Long Dr,,AnotherTown,NJ,000000,,,!!S,!%9,!!#,!!#,"!""." 35ed375c-c226-4879-a789-469cae63383c,"Doe, John",Active,Customer,55281,,28 Short Drive,, Sometown,CA,12345,, email@domain.com,"!$,",!$^,!!#,!!#,!!K a5972bce-408f-4def-b77c-4ae0148dd045,"Duck,Donald",Active,Customer,25,,236 North Main St,,Mytown,PA,11111,,,!!2,!%9,!!#,!!#,"!""."
执行特定任务可能有更多优雅的方法。无论是对我上面的逻辑进行更正,还是以完全不同的方式解决VB.net或PowerShell中的问题,我都是开放的。
答案 0 :(得分:1)
通常,csv可以有多行字段而没有问题。但那些需要被引号括起来。
在您的示例中,这似乎并非如此,但另一方面也没有多行字段,值为Sometown
的字段从新行开始。所以我想知道这是不是原始数据。
如果多行字段 用引号括起来,则需要通知解析器。
但即使使用单行,您也会遇到内部带有分隔符的字段引起的问题。幸运的是,这些都是引用的(因为它们应该是),所以你需要设置TextFieldParser.HasFieldsEnclosedInQuotes
属性。
现在,如果您的多行字段恰好被引用(应该如此),则上述设置应解决所有问题。
<强>更新强>
你可以这样做:
currentRow = MyReader.ReadFields()
If currentRow.Count = 18 Then
strFixedFile.WriteLine(currentRow)
Else
'Write current row without newline
'Read next line/row
'WriteLine this row
End If
但是你必须在内部使用分隔符处理像"Smith,James"
这样的字段。确保您的解析器正确处理引用的字段(参见上文)。
答案 1 :(得分:0)
最直接的方法可能是您第一次验证检查的变体:
我不了解VB.net,但在PowerShell中看起来有点像这样:
$reader = New-Object IO.StreamReader ('C:\path\to\input.csv')
$writer = New-Object IO.StreamWriter ('C:\path\to\output.csv', $false)
$writer.WriteLine($reader.ReadLine()) # copy CSV header
$output = '' # output buffer
$current = '' # pre-buffered current line from input file
while ($reader.Peek() -ge 0) {
# read line into pre-buffer
$current = $reader.ReadLine()
$hasGUID = $current -match '^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12},'
# append line to output buffer if it doesn't have a GUID, otherwise
# write the output buffer to file if it contains data and move the
# current line to the output buffer
if (-not $hasGUID) {
$output += $current
} else {
if ($output) { $writer.WriteLine($output) }
$output = $current
}
}
# write remaining pre-buffered line (if there is one)
if ($current -and $hasGUID) { $writer.WriteLine($current) }
$reader.Close(); $reader.Dispose()
$writer.Close(); $writer.Dispose()