Как я могу разделить файлы 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);
ХТХ,
Ричард