【问题标题】:Want VBA in excel to read very large CSV and create output file of a small subset of the CSV希望 Excel 中的 VBA 读取非常大的 CSV 并创建一小部分 CSV 的输出文件
【发布时间】:2010-09-30 11:03:17
【问题描述】:

我有一个包含 120 万条文本记录的 csv 文件。字母数字字段用引号括起来,日期/时间或数字字段则不用。

例如 "Fred","Smith",01/07/1967,2,"7, The High Street","Anytown","Anycounty","LS1 7AA"

我想要做的是在 Excel 中编写一些 VBA(或多或少是我相当精通使用的唯一可用工具),逐条读取 CSV 记录,执行检查(因为它发生在最后一个字段,邮政编码),然后将 1.2m 记录的一小部分输出到一个新的输出文件。

我了解如何打开这两个文件,读取记录,对数据做我需要做的事情并将其写出来(我将只输出带有表示异常类型的前缀的输入记录)

我不知道如何正确解析 VBA 中的 CSV。我无法进行简单的文本扫描并搜索逗号,因为文本有时包含逗号(因此文本字段是文本分隔的)

是否有一个很棒的命令可以让我快速从记录中的第 n 个字段中获取数据?

我想要的是 s_work = field(s_input_record,5) 其中 5 是我的 CSV 中的字段编号....

非常感谢, C

【问题讨论】:

    标签: vba csv excel


    【解决方案1】:

    下面的代码应该可以解决问题。我面前没有 Excel,所以我没有测试它,但这个概念是合理的。

    如果这最终太慢,我们可以寻找提高效率的方法。

    Sub SelectSomeRecords()
        Dim testLine As String
    
        Open inputFileName For Input As #1
        Open outputFileName For Output As #2
    
        While Not EOF(1)
            Line Input #1, testLine
            If RecordIsInteresting(testLine) Then
                Print #2, testLine
            End If
        Wend
    
        Close #1
        Close #2
    End Sub
    
    Function RecordIsInteresting(recordLine As String) As Boolean
        Dim lineItems(1 to 8) As String
    
        GetRecordItems(lineItems(), recordLine)
    
        ''// do your custom checking here:
        RecordIsInteresting = lineItems(8) = "LS1 7AA"
    End Function
    
    Sub GetRecordItems(items() As String, recordLine as String)
        Dim finishString as Boolean
        Dim itemString as String
        Dim itemIndex as Integer
        Dim charIndex as Long
        Dim inQuote as Boolean
        Dim testChar as String
    
        inQuote = False
        charIndex = 1
        itemIndex = 1
        itemString = ""
        finishString = False
    
        While charIndex <= Len(recordLine)
            testChar = Mid$(recordLine, charIndex, 1)
    
            finishString = False
    
            If inQuote Then
                If testChar = Chr$(34) Then
                    inQuote = False
                    finishString = True
                    charIndex = charIndex + 1 ''// ignore the next comma
                Else
                    itemString = itemString + testChar
                End If
            Else
                If testChar = Chr$(34) Then
                    inQuote = True
                ElseIf testChar = "," Then
                    finishString = True
                Else
                    itemString = itemString + testChar
                End If
            End If
    
            If finishString Then
                items(itemIndex) = itemString
                itemString = ""
                itemIndex = itemIndex + 1
            End If
    
            charIndex = charIndex + 1
        Wend
    End Sub
    

    【讨论】:

      【解决方案2】:

      我使用上面给出的代码的以下衍生代码成功地在 Excel 中从 VBA 打开了任意 csv 文件。

      选项显式
      公共 cn 作为连接
      公共子 DoIt()
      将 strcon 调暗为字符串
      将 strsql 调暗为字符串
      Dim rs As Recordset

      设置 cn = CreateObject("ADODB.Connection")

      strcon = "提供者=Microsoft.Jet.OLEDB.4.0;数据源=C:\bin\HomePlanet\;" _
      & "Extended Properties=""text;HDR=Yes;FMT=Delimited"";"

      cn.Open strcon

      strsql = "SELECT * FROM astuname.csv "
      设置 rs = 新 ADODB.Recordset
      rs.Open strsql, cn
      DoEvents 在这里暂停以检查对象和属性 rs.关闭
      结束子

      rs(记录集)有一个字段集合,带有一个 Count 属性。每个字段都作为一个 Type 属性。

      您可以通过序列号引用字段...

      Debug.Print rs.Fields(rs.Fields.Count - 1).Type

      这就够了吗?

      如果没有,请张贴输入文件的前几行,我将继续使用它。

      【讨论】:

        【解决方案3】:

        任何你可以在 Excel 中用 vba 一次做一行的事情,你都可以用 vba 在 access 中做;加上更多,因为它是一个数据库而不是电子表格。您无法访问吗?

        处理逻辑表、记录和字段比处理逻辑工作表、行和列要容易得多。

        对于输入,为什么“/Data/Import External Data/Text/csv”不起作用?输入不是真正可移植的 csv 吗?

        【讨论】:

        • 120 万行无法在 Excel 中工作,但是,可以使用 Jet 引擎(即 Access 所基于的引擎)使用 ADO 处理数据。因此我的回复。
        • 好的 - 确认一下 - 访问不可用?无论如何,如果您可以使用 ADO 访问 Jet,您还可以使用 Excel VBA 从 Excel 中打开 Access mdb 数据库并将数据存储在那里 - 您甚至不需要安装 Access 来执行此操作。
        【解决方案4】:

        查看 Excel 帮助中的Input # 语句

        示例用法如下:

        Input #fnInput, s_Forename, s_Surname, dt_DOB, i_Something, s_Street, s_Town, s_County, s_Postcode
        

        然后使用Write #语句再次写出匹配的记录

        唯一的问题可能是输出中的日期格式会以 #1967-07-01# 结尾,但这种格式与 01/07/1967 不同,后者表示英国的 7 月 1 日和英国的 1 月 7 日。我们。如果您需要保留日期的格式,请将其写为字符串:

        s_DOB = Format(dt_DOB, "dd/mm/yyyy")
        

        【讨论】:

          【解决方案5】:

          我建议查看正则表达式库(您应该在“工具...参考”中将其视为“Microsoft VBScript 正则表达式 5.5”或类似的内容。

          在这个位置有 Reg Exp 和相当全面的逐字符示例:http://www.xbeat.net/vbspeed/c_ParseCSV.php。请注意,Regexp 版本要短得多!

          玩得开心……

          【讨论】:

            【解决方案6】:

            这并不能直接回答您的问题,但 grep(或 Windows 等效项之一)会为此大放异彩,例如,

            grep -e <regex_filter> foo.csv > bar.csv
            

            【讨论】:

            【解决方案7】:

            VBScript 怎么样,虽然这也适用于 Excel:

            Set cn = CreateObject("ADODB.Connection")
            
            'Note HDR=Yes, that is, first row contains field names '
            'and FMT delimted, ie CSV '
            
            strCon="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\Docs\;" _
            & "Extended Properties=""text;HDR=Yes;FMT=Delimited"";"
            
            cn.open strcon
            
            'You would not need delimiters ('') if last field is numeric: '    
            strSQL="SELECT FieldName1, FieldName2 INTO New.csv FROM Old.csv " _
            & " WHERE LastFieldName='SomeTextValue'"
            
            'Creates new csv file
            cn.Execute strSQL
            

            【讨论】:

            • 我过去使用过类似的技术来完成同样的事情
            • +1 对于如此大的数据集,这是最好的解决方案,我过去曾使用聚合数据完成此操作
            • @Fionnuala,我正在使用您的答案将 CSV 文件读入内存。但是,正如我在一个问题中指出的那样,我遇到了 280 个字符的限制:stackoverflow.com/questions/34230062/…您是否遇到过这种行为以及如何克服这个限制?
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-03-22
            • 2013-05-07
            • 1970-01-01
            • 2015-06-02
            相关资源
            最近更新 更多