【问题标题】:Open CSV file via VBA (performance)通过 VBA 打开 CSV 文件(性能)
【发布时间】:2023-04-05 17:40:01
【问题描述】:

显然,这个问题已经被问过很多次了。正常程序:

Workbooks.Open (ActiveWorkbook.Path & "\Test.csv")

无法正确解析 CSV(一个单元格中有很多行)

感谢 Lernkurve,我可以使用他的函数来解决问题:Opening semicolon delimited CSV file

Sub ImportCSVFile(filepath As String)
Dim line As String
Dim arrayOfElements
Dim linenumber As Integer
Dim elementnumber As Integer
Dim element As Variant

linenumber = 0
elementnumber = 0

Open filepath For Input As #1 ' Open file for input
    Do While Not EOF(1) ' Loop until end of file
        linenumber = linenumber + 1
        Line Input #1, line
        arrayOfElements = Split(line, ";")

        elementnumber = 0
        For Each element In arrayOfElements
            elementnumber = elementnumber + 1
            Cells(linenumber, elementnumber).Value = element
        Next
    Loop
Close #1 ' Close file.
End Sub

但这并不快(我有数千列的文件),我的问题是:

有什么本地方法可以通过正确解析在 Excel 中打开 CSV 文件吗?

【问题讨论】:

  • CSV 文件的大小(以 MB 为单位)以及您希望操作的最长持续时间是多少?
  • 大约 450 kb /file 和大约 14 个文件。由于此宏的目标是供无法编码的人使用,因此必须完全自动化且快速,因此用户无需等待。
  • 如果它是 450kb,那么我在答案中给出的功能应该会在 3 秒内打开并复制 14 个文件的内容,可能更短。
  • 那么完美。会尝试的。谢谢楼主

标签: excel vba


【解决方案1】:

Workbooks.Open 也可以。

Workbooks.Open ActiveWorkbook.Path & "\Temp.csv", Local:=True

这是必要的,因为我在德国使用 Excel,而 Excel 默认使用“,”来分隔 .csv,因为我使用英文安装的 Windows。即使您使用下面的代码,excel 也会强制使用“,”分隔符。

Workbooks.Open ActiveWorkbook.Path & "\Test.csv", , , 6, , , , , ";"

Workbooks.Open ActiveWorkbook.Path & "\Temp.csv", , , 4 +这个变种不起作用(!)

如果被 Local 参数阻止,为什么他们甚至还有 delimiter 参数?!这根本没有意义。但现在可以了。

【讨论】:

    【解决方案2】:

    此函数读取 15MB 的 CSV 文件,并在大约 3 秒内将其内容复制到工作表中。 在您的代码中可能花费大量时间的是您逐个单元格地复制数据而不是一次放置整个内容。

    Option Explicit
    
    Public Sub test()
    
      copyDataFromCsvFileToSheet "C:\temp\test.csv", ",", "Sheet1"
    
    End Sub
    
    Private Sub copyDataFromCsvFileToSheet(parFileName As String, parDelimiter As String, parSheetName As String)
    
      Dim data As Variant
    
      data = getDataFromFile(parFileName, parDelimiter)
      If Not isArrayEmpty(data) Then
        With Sheets(parSheetName)
          .Cells.ClearContents
          .Cells(1, 1).Resize(UBound(data, 1), UBound(data, 2)) = data
        End With
      End If
    
    End Sub
    
    Public Function isArrayEmpty(parArray As Variant) As Boolean
    'Returns false if not an array or dynamic array that has not been initialised (ReDim) or has been erased (Erase)
    
      If IsArray(parArray) = False Then isArrayEmpty = True
      On Error Resume Next
      If UBound(parArray) < LBound(parArray) Then isArrayEmpty = True: Exit Function Else: isArrayEmpty = False
    
    End Function
    
    Private Function getDataFromFile(parFileName As String, parDelimiter As String, Optional parExcludeCharacter As String = "") As Variant
    'parFileName is supposed to be a delimited file (csv...)
    'parDelimiter is the delimiter, "," for example in a comma delimited file
    'Returns an empty array if file is empty or can't be opened
    'number of columns based on the line with the largest number of columns, not on the first line
    'parExcludeCharacter: sometimes csv files have quotes around strings: "XXX" - if parExcludeCharacter = """" then removes the quotes
    
    
      Dim locLinesList() As Variant
      Dim locData As Variant
      Dim i As Long
      Dim j As Long
      Dim locNumRows As Long
      Dim locNumCols As Long
      Dim fso As Variant
      Dim ts As Variant
      Const REDIM_STEP = 10000
    
      Set fso = CreateObject("Scripting.FileSystemObject")
    
      On Error GoTo error_open_file
      Set ts = fso.OpenTextFile(parFileName)
      On Error GoTo unhandled_error
    
      'Counts the number of lines and the largest number of columns
      ReDim locLinesList(1 To 1) As Variant
      i = 0
      Do While Not ts.AtEndOfStream
        If i Mod REDIM_STEP = 0 Then
          ReDim Preserve locLinesList(1 To UBound(locLinesList, 1) + REDIM_STEP) As Variant
        End If
        locLinesList(i + 1) = Split(ts.ReadLine, parDelimiter)
        j = UBound(locLinesList(i + 1), 1) 'number of columns
        If locNumCols < j Then locNumCols = j
        i = i + 1
      Loop
    
      ts.Close
    
      locNumRows = i
    
      If locNumRows = 0 Then Exit Function 'Empty file
    
      ReDim locData(1 To locNumRows, 1 To locNumCols + 1) As Variant
    
      'Copies the file into an array
      If parExcludeCharacter <> "" Then
    
        For i = 1 To locNumRows
          For j = 0 To UBound(locLinesList(i), 1)
            If Left(locLinesList(i)(j), 1) = parExcludeCharacter Then
              If Right(locLinesList(i)(j), 1) = parExcludeCharacter Then
                locLinesList(i)(j) = Mid(locLinesList(i)(j), 2, Len(locLinesList(i)(j)) - 2)       'If locTempArray = "", Mid returns ""
              Else
                locLinesList(i)(j) = Right(locLinesList(i)(j), Len(locLinesList(i)(j)) - 1)
              End If
            ElseIf Right(locLinesList(i)(j), 1) = parExcludeCharacter Then
              locLinesList(i)(j) = Left(locLinesList(i)(j), Len(locLinesList(i)(j)) - 1)
            End If
            locData(i, j + 1) = locLinesList(i)(j)
          Next j
        Next i
    
      Else
    
        For i = 1 To locNumRows
          For j = 0 To UBound(locLinesList(i), 1)
            locData(i, j + 1) = locLinesList(i)(j)
          Next j
        Next i
    
      End If
    
      getDataFromFile = locData
    
      Exit Function
    
    error_open_file:             'returns empty variant
    unhandled_error:             'returns empty variant
    
    End Function
    

    【讨论】:

    • 哇,这样的任务有很多代码。我将通过它来完全理解它。所以我想我最初问题的答案是“不,vba 不提供正确读取 csv 的本机函数”。感谢您的努力!
    • @assylias 有没有办法将您的代码与 Locals=true 一起使用?它弄乱了我的数字格式,分隔符工作正常
    • 该代码只读取字符并且对数字格式一无所知 - 如果文件包含格式化的数字(例如 1,000,000 代表 100 万),您最好使用其他选项,例如接受的答案(使用 Workbooks .打开)。
    【解决方案3】:

    这可能会对您有所帮助,也取决于您的 CSV 文件的格式。

    1. 打开您的 Excel 表格并转到菜单 Data > Import External Data > Import Data
    2. 选择您的CSV 文件。
    3. 原始数据类型:选择Fixed width,然后选择Next
    4. 它将自动分隔您的columns。然后,您可以检查Data preview 面板中的拆分列。
    5. 然后Finish 看看。

    注意:您也可以使用 Delimited 作为原始数据类型。 在这种情况下,您需要键入分隔符。

    HTH!

    【讨论】:

    • 与上述相同的问题。这不是 vba 编码,我不赞成使用 automacros(因为它在大多数情况下添加了更多的代码)。希望你不要因此而恨我!感谢您的帮助!
    【解决方案4】:

    你试过import text function吗?

    【讨论】:

    • 我手动打开 csv 没有任何问题(例如 file->open 或您提到的导入文本功能)。仅通过 VBA 代码打开 csv 不能正常工作,我需要使用 VBA。至少我不明白如何在 VBA 中使用它。不过还是谢谢你的帮助!
    • 您是否尝试过在使用导入向导时录制宏?
    • 好的,用录音试了一下,效果很好。但它不够动态。我可以改进它,但我也可以从头开始编写它。我只是不喜欢汽车制造。
    • 从头开始编写可能比使用内置 Excel 函数慢得多,因为 Excel 函数总是快得多。使用宏记录器中的代码没有错,这有助于理解 Excel 对象:) - 请记住,您总是需要优化它
    【解决方案5】:

    我有同样的问题,我无法在 Excel 中打开 CSV 文件。我在这个问题Opening a file in excel via Workbooks.OpenText

    中找到了适合我的解决方案

    这个问题帮助我找到了适合我的代码。代码看起来或多或少是这样的:

    Private Sub OpenCSVFile(filename as String)
        Dim datasourceFilename As String
        Dim currentPath As String
    
        datasourceFilename = "\" & filename & ".csv"
        currentPath = ActiveWorkbook.Path
    
        Workbooks.OpenText Filename:=currentPath & datasourceFilename, _
                           Origin:=xlWindows, _
                           StartRow:=1, _
                           DataType:=xlDelimited, _
                           TextQualifier:=xlDoubleQuote, _
                           ConsecutiveDelimiter:=False, _
                           Tab:=False, _
                           Semicolon:=False, _
                           Comma:=True, _
                           Space:=False, _
                           Other:=False, _
                           FieldInfo:=Array(Array(1, 1), Array(2, 1)), _
                           DecimalSeparator:=".", _
                           ThousandsSeparator:=",", _
                           TrailingMinusNumbers:=True
    End Sub
    

    至少,它帮助我了解了很多可以用于Workbooks.OpenText 方法的参数。

    【讨论】:

      【解决方案6】:

      有时,无论设置多少参数,Workbooks.open 的所有解决方案都无法正常工作。 对我来说,最快的解决方案是更改区域和语言设置中的列表分隔符。 区域窗口/附加设置.../列表分隔符。

      如果 csv 没有以正确的方式打开 您可能已将 ',' 设置为列表分隔符。只需将其更改为';'一切都解决了。 当“一切都对你不利”时,这是最简单的方法:P

      【讨论】:

        猜你喜欢
        • 2018-03-13
        • 2013-11-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-25
        • 1970-01-01
        • 1970-01-01
        • 2014-05-30
        相关资源
        最近更新 更多