批处理文件用于拆分 .csv 文件

35
我有一个非常大的 .csv 文件(>500MB),希望在命令提示符中将其分割成较小的 .csv 文件。(基本上是想在 Windows 中找到一个类似于 Linux "split" 的功能。)
这必须是一个批处理脚本,因为我的计算机只安装了 Windows,请求软件很麻烦。我找到了许多示例代码(http://forums.techguy.org/software-development/1023949-split-100000-line-csv-into.html),但是当我执行批处理时它并不起作用。我只得到一个输出文件,大小仅为 125KB,而我要求它解析每 20000 行。
是否有人遇到过类似的问题,如何解决?

如果你正在使用Windows,那么它不是DOS。你说的是Windows命令提示符(cmd.exe)。 - dbenham
使用GnuWin CoreUtils中的split命令吗? - seumas
我曾经考虑过,但是在我的工作场所获取软件很麻烦。多亏了所有做出贡献的人,这个问题已经得到解决。 - SeekingAlpha
6个回答

44

试试这个:

@echo off
setLocal EnableDelayedExpansion

set limit=20000
set file=export.csv
set lineCounter=1
set filenameCounter=1

set name=
set extension=
for %%a in (%file%) do (
    set "name=%%~na"
    set "extension=%%~xa"
)

for /f "tokens=*" %%a in (%file%) do (
    set splitFile=!name!-part!filenameCounter!!extension!
    if !lineCounter! gtr !limit! (
        set /a filenameCounter=!filenameCounter! + 1
        set lineCounter=1
        echo Created !splitFile!.
    )
    echo %%a>> !splitFile!

    set /a lineCounter=!lineCounter! + 1
)

如上所示的代码将把原始csv文件拆分为多个csv文件,每个文件限制为20,000行。你只需要相应地更改!file!!limit!变量即可。希望这有所帮助。


1
在我的电脑上(核心i7),每兆字节大约需要30秒钟。给它4个小时,它就会分割您的500 MB csv文件。 - indiv
谢谢Dale。不幸的是,这段代码生成了与我之前提供的链接相同的输出文件。生成的文件大小为126kb,文件名也相同,内容几乎与我之前完成的相同。 - SeekingAlpha
1
你可以通过在初始化时设置 lineCounter=1+%limit%filenameCounter=0 来加快速度;然后将 set splitfile 移动到 if 语句块中,在增加了 filenameCounter 后。这样,文件名每 20,000 行才会被设置一次,而不是每行都设置一次... - Magoo
3
遗憾的是,我认为这个方案注定会失败。环境变量所能包含的字符串长度有限(约为8K),如果你有1000列,即使每列的数据量不多,也很容易超出这个限制——列分隔符本身就占用了1000个字符!除此之外,Dale的方案似乎是可行的... - Magoo
1
谢谢您的脚本。有没有可能改进它来处理带有UTF8字符的CSV文件? - Mark
显示剩余8条评论

41

谢谢,太简单了! - ThisClark
不错的程序。快速且易用。 - Tom Collins
非常棒的工具,完全满足了我的需求,速度超快且易于使用。 - ammills01
太棒了,无需安装。 - drtf
这对我也起作用了。正如其他人所说,无需安装。使用Malwarebytes Antimalware进行扫描,一切都很好。很好它是一个可执行文件,可以与使用受支持版本的Windows的其他人共享,不必记住要使用什么脚本来拆分CSV,我用它做了什么等等。 - notacouch
显示剩余3条评论

28

使用cgwin命令SPLIT。 示例

将文件每500行分割:

split -l 500 [filename.ext]

默认情况下,在扩展名后面添加xa、xb、xc等内容。

要生成以数字结尾并带有正确扩展名的文件,请使用以下方法:

split -l 1000 sourcefilename.ext destinationfilename -d --additional-suffix=.ext

-d或-l的位置不重要,

  • "-d"--numeric-suffixes相同
  • "-l"--lines相同

更多信息请参阅split --help


4
如果要拆分非常大的文件,我找到的解决方法是从这里适应而来,并在批处理文件中“嵌入”PowerShell。相比我尝试过的其他方法,这个方法速度很快(我不清楚这里发布的其他选项)。
使用下面的mysplit.bat的方法是:

mysplit.bat <mysize> 'myfile'

注意:脚本旨在使用第一个参数作为拆分大小。它当前硬编码为100Mb。修复这个问题应该不难。
注意2:文件名应该用单引号括起来。其他引用方式似乎无效。
注意3:它会按给定的字节数拆分文件,而不是按给定行数拆分。对我来说,这已经足够好了。也许可以添加一些代码来完善每个块读取,直到下一个CR/LF。这将完整地拆分每一行(不是固定数量的行),而不会牺牲处理时间。
脚本mysplit.bat:
@REM Using https://dev59.com/lmIk5IYBdhLWcg3wYtJC
@REM and https://dev59.com/xHNA5IYBdhLWcg3wVcT6
@PowerShell  ^
    $upperBound = 100MB;  ^
    $rootName = %2;  ^
    $from = $rootName;  ^
    $fromFile = [io.file]::OpenRead($from);  ^
    $buff = new-object byte[] $upperBound;  ^
    $count = $idx = 0;  ^
    try {  ^
        do {  ^
            'Reading ' + $upperBound;  ^
            $count = $fromFile.Read($buff, 0, $buff.Length);  ^
            if ($count -gt 0) {  ^
                $to = '{0}.{1}' -f ($rootName, $idx);  ^
                $toFile = [io.file]::OpenWrite($to);  ^
                try {  ^
                    'Writing ' + $count + ' to ' + $to;  ^
                    $tofile.Write($buff, 0, $count);  ^
                } finally {  ^
                    $tofile.Close();  ^
                }  ^
            }  ^
            $idx ++;  ^
        } while ($count -gt 0);  ^
    }  ^
    finally {  ^
        $fromFile.Close();  ^
    }  ^
%End PowerShell%

1
csv 按字节数拆分可能不是最好的想法。行不应该被拆分。 - Stephan
@Stephan - 我认为你的说法可能是对的,也可能是不对的。这完全取决于一个人的需求。我实际上需要将一个巨大的csv文件分割,并且按照指定的行号或指定的字节数进行分割同样有效。这实际上是我的需求促使我编写这个脚本的原因。 - sancho.s ReinstateMonicaCellio
当然这取决于你的需求。只是想提醒一下,csv 文件在某行中间以值结尾或开头似乎对我来说是个噩梦。但如果这不重要的话,这似乎是一个很好的答案(也是唯一一个按大小而非行号进行拆分的答案)。 - Stephan
@Stephan - 如果有任何权衡(实际上是否存在权衡),速度与“准确性”(以字节计数为代价牺牲行数)可能是值得的。此评论引用了接受答案的500 MB csv文件分割,在i7(未知速度)中花费了4个小时。我使用我的脚本在i5上分割了一个4.5GB的txt文件,大约2分钟就完成了。 - sancho.s ReinstateMonicaCellio

1

我在寻找类似解决方案时发现了这个问题。我修改了@Dale给出的答案以适应我的需求。我想要的是更灵活一些并且有一些错误处理机制。只是觉得我可以把它放在这里,供任何寻找相同解决方案的人使用。

@echo off
setLocal EnableDelayedExpansion
GOTO checkvars

:checkvars
    IF "%1"=="" GOTO syntaxerror
    IF NOT "%1"=="-f"  GOTO syntaxerror
    IF %2=="" GOTO syntaxerror
    IF NOT EXIST %2 GOTO nofile
    IF "%3"=="" GOTO syntaxerror
    IF NOT "%3"=="-n" GOTO syntaxerror
    IF "%4"==""  GOTO syntaxerror
    set param=%4
    echo %param%| findstr /xr "[1-9][0-9]* 0" >nul && (
        goto proceed
    ) || (
        echo %param% is NOT a valid number
        goto syntaxerror
    )

:proceed
    set limit=%4
    set file=%2
    set lineCounter=1+%limit%
    set filenameCounter=0

    set name=
    set extension=

    for %%a in (%file%) do (
        set "name=%%~na"
        set "extension=%%~xa"
    )

    for /f "usebackq tokens=*" %%a in (%file%) do (
        if !lineCounter! gtr !limit! (
            set splitFile=!name!_part!filenameCounter!!extension!
            set /a filenameCounter=!filenameCounter! + 1
            set lineCounter=1
            echo Created !splitFile!.
        )
        cls
        echo Adding Line !splitFile! - !lineCounter!
        echo %%a>> !splitFile!
        set /a lineCounter=!lineCounter! + 1
    )
    echo Done!
    goto end
:syntaxerror
    Echo Syntax: %0 -f Filename -n "Number Of Rows Per File"
    goto end
:nofile
    echo %2 does not exist
    goto end
:end

0

这将在newfile1.csv中给出行1到20000
并在文件newfile2.csv中给出行20001到结尾

它还克服了每行8K字符的限制。

这使用一个名为findrepl.bat的辅助批处理文件 - https://www.dropbox.com/s/rfdldmcb6vwi9xc/findrepl.bat

findrepl.bat放置在与批处理文件相同的文件夹中或路径上。

它比普通批处理文件更健壮,速度也更快。

findrepl /o:1:20000 <file.csv >newfile1.csv
findrepl /o:20001   <file.csv >newfile2.csv

此链接没有任何东西。 - Ashwani Panwar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接