如何在运行时按文件大小分割CSV并自动保存有效分片?
将CSV按1GB分片并动态保存所有分片的解决方案
问题场景
需要将CSV文件分割为1GB大小的分片,由于文件大小不固定,固定数量分割的方式不适用。已实现预估输出CSV大小和数据集分片逻辑,但不知道如何在运行时动态保存所有分片,避免生成空输出(例如原文件不足2GB时,不要生成第三分片)。已考虑despray操作但代码简化未包含,询问遗漏的实现点。
以下是在HPCC Systems Playground运行的原始代码:
STRING sInputFile := '~class::afca::crimes_-_2001_to_present.csv'; // Specification modLayouts := MODULE EXPORT STRING sTerminator := '\n'; EXPORT lLine := {STRING Line}; EXPORT lTaggedLine := {lLine, UNSIGNED UpToSize, UNSIGNED Part}; END; dInput := DATASET(sInputFile, modLayouts.lLine, CSV(HEADING(0),SEPARATOR(''),TERMINATOR(modLayouts.sTerminator),QUOTE(''),UNICODE) ); // Header-Data split dProduct := dInput[2..]; STRING sHeader := dInput[1].line + modLayouts.sTerminator; // Split config UNSIGNED uOneGigabyte := 1000000000; UNSIGNED uGigabytesLimit := 1; UNSIGNED uBytesLimit := uGigabytesLimit * uOneGigabyte; UNSIGNED uByteCount(STRING sLine) := LENGTH(sLine)+LENGTH(modLayouts.sTerminator); // Partition dInputTagged := ITERATE(PROJECT(dProduct,TRANSFORM(modLayouts.lTaggedLine,SELF:=LEFT)), TRANSFORM(modLayouts.lTaggedLine, SELF.Line := RIGHT.Line, SELF.UpToSize := LEFT.UpToSize + uByteCount(RIGHT.Line), SELF.Part := IF(LEFT.Part=0,1,IF(SELF.UpToSize <= LEFT.Part*(uBytesLimit), LEFT.Part, LEFT.Part+1)), ), modLayouts.lTaggedLine{Part:=0, UpToSize:=0} // 明确初始值,避免默认值歧义 ); // Results SET OF UNSIGNED suParts := SET(SORT(TABLE(dInputTagged,{Part},Part,MERGE),Part),Part); dPart(UNSIGNED uPart) := TABLE(dInputTagged(Part=uPart),{Line}); // Read outs UNSIGNED uEstBytes := SUM(dInputTagged,uByteCount(dInputTagged.Line)); REAL rEstGigas := ROUND(uEstBytes/uOneGigabyte,2); // Save and despray aSavePartNum(UNSIGNED uPart) := FUNCTION RETURN IF(uPart IN suParts, OUTPUT(dPart(uPart),,sInputFile+'_pt'+uPart+'.csv', CSV(HEADING(SINGLE, sHeader),SEPARATOR(''),TERMINATOR('\n'),QUOTE(''),UNICODE) ,compressed,overwrite,expire(1),named('dPart_'+uPart))); END; // Outputs OUTPUT(CHOOSEN(dInputTagged,1000), named('dInputTagged_sample')); OUTPUT(sHeader, named('sHeader')); OUTPUT(rEstGigas, named('rEstGigas')); OUTPUT(uEstBytes, named('uEstBytes')); OUTPUT(suParts, named('suParts')); // 硬编码调用,导致可能生成空文件 aSavePartNum(1); aSavePartNum(2); aSavePartNum(3);
解决方案
核心问题
你当前硬编码调用了aSavePartNum(1);aSavePartNum(2);aSavePartNum(3);,这会强制尝试处理不存在的分片,进而生成空文件。解决思路是遍历实际存在的分片集合,动态触发保存操作。
修改步骤
- 移除硬编码的分片调用,替换为
FOREACH遍历suParts集合:
// 遍历所有实际存在的分片,动态保存 FOREACH suParts AS uPart aSavePartNum(uPart); END;
- (可选)优化
ITERATE的初始值,明确指定初始的Part和UpToSize,避免默认值带来的逻辑歧义(已在上面的代码示例中修正)。
优化后效果
- 只会处理
suParts中存在的分片编号,完全根据原文件大小动态生成对应数量的分片,不会生成空文件。 - 无论原文件是1.1GB、2.8GB还是更小,都能自动适配分片数量。
内容的提问来源于stack exchange,提问作者Emerson Grisi
相关产品推荐
相关产品推荐

