Как я могу разделить файлы CSV на несколько файлов CSV во время выполнения в зависимости от размера файла?


Я пытался разбить CSV-файлы на части по 1 ГБ, и размер файла продукта варьируется, поэтому разбивать на фиксированное число будет бесполезно.
Мне удалось найти способ оценить размер результирующего CSV и получить секционированные наборы данных, но я не понимаю, как сохранить наборы данных в виде логических файлов и распаковать их без ограничения количества частей или спама. выходы.
В этом случае третья часть набора данных будет пустой, так как исходный файл имеет размер менее 2 ГБ, а третья часть будет занимать третий гигабайт, которого не существует.
Добавление действия despray не проблема, его нет из-за упрощения кода.
Что мне не хватает? Что я мог бы использовать для сохранения всех частей набора данных, определенных во время выполнения?
Ниже приведен код, который я запускал на [http://play.hpccsystems.com:8010/][1].

      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,SELF:=[])),
     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)),
        )
    );
// 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);

1 ответ

Вот пример того, как я бы это сделал:

          //This first bit just generates garbage records to play with:
IMPORT STD;

GenStr(NumChars) := FUNCTION
  Ltrs    := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  OneChar := Ltrs[RANDOM() % 52 + 1];
  LtrD    S   := DATASET([{''}],{STRING res});
  ResDS   := NORMALIZE(LtrDS,
                           NumChars,
                       TRANSFORM({STRING res},
                                 SELF.res := OneChar));
  ResRec  := ROLLUP(ResDS,TRUE,
                    TRANSFORM({STRING res},
                              SELF.Res := TRIM(LEFT.Res) + RIGHT.Res));
  RETURN ResRec[1].Res;                           
END;  
  
//generate input records        
Rec := RECORD
  UNSIGNED8 RecID;
  STRING100 Str1;
  STRING100 Str2;
  STRING100 Str3;
END;
RecsPerNode := 200;
ds := DATASET(RecsPerNode,
              TRANSFORM(Rec,
                        SELF.RecID := (RecsPerNode * STD.system.Thorlib.Node()) + COUNTER,
                        SELF.Str1  := GenStr((RANDOM() + COUNTER) % 100 + 1),
                        SELF.Str2  := GenStr((RANDOM() + COUNTER) % 100 + 1),
                        SELF.Str3  := GenStr((RANDOM() + COUNTER) % 100 + 1)
                       ),
              LOCAL);
//*******************************************
//rest of the code is the chunk output process:     

//generate CSV file chunk rec points
MaxFileSize := 10000;  //maximum size of each output CSV file

{UNSIGNED len, UNSIGNED RecNum} BldCSVline(ds L, INTEGER C) := TRANSFORM
  line := TRIM((STRING10)L.RecID) + ',' + TRIM(L.Str1) + ',' + TRIM(L.Str3) + ',' + TRIM(L.Str3);
  SELF.len  := LENGTH(TRIM(Line)) + 2; //add 2 bytes for CRLF terminator
  SELF.RecNum := C;
END;
pds := PROJECT(ds,BldCSVline(LEFT,COUNTER));

Chunks := ROLLUP(pds,
                 LEFT.len + RIGHT.len < MaxFileSize,
                 TRANSFORM({pds},
                           SELF.len := LEFT.len + RIGHT.len,
                           SELF := RIGHT ));
Chunk1 := Chunks[1].Recnum;
Chunk2 := Chunks[2].Recnum;
Chunk3 := Chunks[3].Recnum;
Chunk4 := Chunks[4].Recnum;
OUTPUT(ds[1 .. Chunk1],,'~RTTEST::CSVtest1',CSV,OVERWRITE);
OUTPUT(ds[Chunk1+1 .. Chunk2],,'~RTTEST::CSVtest2',CSV,OVERWRITE);
OUTPUT(ds[Chunk2+1 .. Chunk3],,'~RTTEST::CSVtest3',CSV,OVERWRITE);
OUTPUT(ds[Chunk3+1 .. Chunk4],,'~RTTEST::CSVtest4',CSV,OVERWRITE);

ХТХ,

Ричард

Другие вопросы по тегам