大CSV解析

大CSV解析,csv,vbscript,Csv,Vbscript,我很难想出一个健壮而轻巧的算法来处理一些大的CSV文件。下面是一个简单的示例,展示了它们的外观: Time a b c 0 2.9 1.6 4.1 0 3.6 1.1 0.5 0 3.4 0.2 1.7 1.2 0.1 4.2 1.9 1.201 2.3 3.1 4.8 9.99 0.2 0.8 1.2 10 3.1 3.3 2.3 10 3.6 3.5

我很难想出一个健壮而轻巧的算法来处理一些大的CSV文件。下面是一个简单的示例,展示了它们的外观:

Time a b c 0 2.9 1.6 4.1 0 3.6 1.1 0.5 0 3.4 0.2 1.7 1.2 0.1 4.2 1.9 1.201 2.3 3.1 4.8 9.99 0.2 0.8 1.2 10 3.1 3.3 2.3 10 3.6 3.5 3.0 10.01 1.1 4.5 3.9 10.01 2.2 3.0 2.3 17 4.3 2.3 3.8 20 1.0 3.2 3.0 30 4.1 3.0 4.9 40 3.8 3.3 1.6 时间a b c 0 2.9 1.6 4.1 0 3.6 1.1 0.5 0 3.4 0.2 1.7 1.2 0.1 4.2 1.9 1.201 2.3 3.1 4.8 9.99 0.2 0.8 1.2 10 3.1 3.3 2.3 10 3.6 3.5 3.0 10.01 1.1 4.5 3.9 10.01 2.2 3.0 2.3 17 4.3 2.3 3.8 20 1.0 3.2 3.0 30 4.1 3.0 4.9 40 3.8 3.3 1.6 我需要根据以下规则对我的CSV进行后处理:

  • 只需考虑时间为10倍的线路
  • 如果多行具有相同的时间夯实,则取不同行中每列的平均值
  • 以下是我希望得到的输出:

    Time a b c 0 3.3 0.97 2.1 10 2.04 3.02 2.54 20 1.0 3.2 3.0 30 4.1 3.0 4.9 40 3.8 3.3 1.6 时间a b c 0 3.3 0.97 2.1 10 2.04 3.02 2.54 20 1.0 3.2 3.0 30 4.1 3.0 4.9 40 3.8 3.3 1.6 现在的限制是:我的脚本需要在没有足够内存的Windows机器上处理相当大的CSV(高达数百MB)。正因为如此,我并不热衷于将所有CSV存储在一个大的字典数组中,但我更喜欢逐行存储

    这是我第一次天真的尝试。它很差,不能正常工作。(小幅注释:平均值不是真实的平均值,而是一种奇怪的“运行平均值”。请记住,我试图评估工作流程,但在现阶段并不真正关心数字。)

    filename=“测试”
    采样时间=10.0
    公差=1e-1
    Dim FSO,输入,输出
    常数ForReading=1
    写入常数=2
    '创建对象
    设置FSO=CreateObject(“Scripting.FileSystemObject”)
    设置输入=FSO.OpenTextFile(文件名和“.csv”,用于读取,False)
    设置output=FSO.OpenTextFile(文件名&“\u output.csv”,用于写入,True)
    '第一行:写入标题
    s=input.ReadLine()
    output.WriteLines
    '第二行:初始化sSplit_old
    s=input.ReadLine()
    sSplit=拆分(s,“,”)
    sSplit_old=sSplit
    “继续读。。。
    直到input.AtEndOfStream
    '读取新行并将其拆分为组件
    '读取行的第一个元素(即时间)时需要此参数
    s=input.ReadLine()
    sSplit=拆分(s,“,”)
    '如果剩余时间/采样时间低于公差,则
    “行必须进行处理。
    '这里是“\”运算符(即整数除法:5\2=2,而5/2=2.5)
    '用作“Mod”运算符返回整数余数。
    如果CDbl(sSplit(0))-采样时间*(CDbl(sSplit(0))\采样时间)<公差,则
    '如果当前时间接近上一个时间(在公差范围内)。。。
    如果Abs(CDbl(sSplit(0))-CDbl(sSplit_old(0))<公差,则
    '... 循环遍历阵列并存储平均值
    对于i=0至UBound(sSplit)
    sSplit_old(i)=(CDbl(sSplit(i))+CDbl(sSplit_old(i))/2.0
    下一个
    其他的
    '... 否则,只需写上一次并保存当前时间
    “一个用来和下一个比较
    s=连接(sSplit_old,“,”)
    output.WriteLines
    sSplit_old=sSplit
    如果结束
    如果结束
    环
    output.WriteLines
    输入,关闭
    输出,关闭
    
    当您为Windows操作系统支付了(太多)费用时,您也为SQL引擎支付了费用。所以使用它:

    Option Explicit
    
    Dim db : Set db = CreateObject("ADODB.Connection")
    Dim dd : dd = "E:\work\proj\soa\47155733\data"
    Dim cs
    If "AMD64" = CreateObject("WScript.Shell").ExpandEnvironmentStrings("%PROCESSOR_ARCHITECTURE%") Then
       cs = "Driver=Microsoft Access Text Driver (*.txt, *.csv);Dbq=" & dd & ";Extensions=asc,csv,tab,txt;"
       WScript.Echo "64 Bit:", cs
    Else
       cs = "Driver={Microsoft Text Driver (*.txt; *.csv)};Dbq=" & dd & ";Extensions=asc,csv,tab,txt;"
       WScript.Echo "32 Bit:", cs
    End If
    db.Open cs
    
    Dim ss : ss = "SELECT * FROM [47155733.txt]"
    WScript.Echo ss
    WScript.Echo db.Execute(ss).GetString(2,,vbTab,vbCrlf,"*")
    
    ss =   "SELECT t, avg(a), avg(b), avg(c) FROM [47155733.txt]" _
         & " WHERE t = Int(t) And 0.0 = t Mod 10 GROUP BY t"
    WScript.Echo ss
    WScript.Echo db.Execute(ss).GetString(2,,vbTab,vbCrlf,"*")
    
    ss = "SELECT Round(1/3, 3)"
    WScript.Echo ss
    WScript.Echo db.Execute(ss).GetString(2,,vbTab,vbCrlf,"*")
    
    输出:

    cscript 47155733.vbs
    SELECT * FROM [47155733.txt]
    0       2,9     1,6     4,1
    0       3,6     1,1     0,5
    0       3,4     0,2     1,7
    1,2     0,1     4,2     1,9
    1,201   2,3     3,1     4,8
    9,99    0,2     0,8     1,2
    10      3,1     3,3     2,3
    10      3,6     3,5     3
    10,01   1,1     4,5     3,9
    10,01   2,2     3       2,3
    17      4,3     2,3     3,8
    20      1       3,2     3
    30      4,1     3       4,9
    40      3,8     3,3     1,6
    
    SELECT t, avg(a), avg(b), avg(c) FROM [47155733.txt] WHERE t = Int(t) And 0.0 = t Mod 10 GROUP BY t
    0       3,3     0,966666666666667       2,1
    10      3,35    3,4     2,65
    20      1       3,2     3
    30      4,1     3       4,9
    40      3,8     3,3     1,6
    
    SELECT Round(1/3, 3)
    0,333
    
    在Windows 10上测试32位和64位;德国地区。我更喜欢在schema.ini文件中指定文件格式:

    [47155733.txt]
    Format=Delimited(,)
    ColNameHeader=True
    DecimalSymbol=.
    Col1=t Double
    Col2=a Double
    Col3=b Double
    Col4=c Double
    
    背景:


    ,.

    如果在电子表格中执行此操作,会更容易。VBA是你的选择吗?不是。我需要将其作为自动化脚本的一部分,该脚本需要在生成csv的机器上运行。这是我正在编写的一个自动化脚本的一部分,用于从一个巨大的数据文件到一个较小的即用数据集。由于您没有提到代码的特定问题(需要更好的算法),我建议您将此移到codereview.stackexchange.com您的代码“工作不正常”的原因是什么?是的。字段之间的单个分隔符,而不是问题中显示的内容。教训:不要美化你的数据样本。把你的数据准确地显示出来,这可能会对如何解决一项任务产生根本性的影响。这正是我要提出的。应该添加到VBScript中的一件事是
    SetLocale“en-us”
    (或OP喜欢的任何内容),以控制数字在字符串中的输出方式。OP说,预期的文件为几百MB,有数百列。ADODB的速度可能非常慢(并且在内存有限的情况下可能根本无法工作)。
    [47155733.txt]
    Format=Delimited(,)
    ColNameHeader=True
    DecimalSymbol=.
    Col1=t Double
    Col2=a Double
    Col3=b Double
    Col4=c Double