Autor Téma: Jak načíst část souboru?  (Přečteno 4197 krát)

Offline vangog

  • Hrdina
  • ****
  • Příspěvků: 422
  • Karma: 0
    • Verze Delphi: 7
Jak načíst část souboru?
« kdy: 30-06-2018, 13:05:41 »
Jak to mám udělat v Delphi 7 když potřebuju načíst jenom část souboru? Soubor který má přes 3.7 GB bych potřeboval cyklicky načítat po menších částech např. po 200KB a tyto, poté co vyextrahuju data, pak zapisovat do jiného souboru stylem "přidat" do souboru.

100401

  • Host
Re:Jak načíst část souboru?
« Odpověď #1 kdy: 30-06-2018, 14:51:35 »
Pouzij memory mapped file (ukazek pro Delphi budou mraky, doporucil bych prohledat Stack Overflow).

Offline vangog

  • Hrdina
  • ****
  • Příspěvků: 422
  • Karma: 0
    • Verze Delphi: 7
Re:Jak načíst část souboru?
« Odpověď #2 kdy: 30-06-2018, 15:56:22 »
Už jsem použil tohle:
Kód: Delphi [Vybrat]
  1. var
  2.   SS,ST: TFileStream;
  3.   Buffer: string;
  4.   sf,tf,TempStr: string;
  5.   i: Integer;
  6. begin
  7.   sf := 'U:\SYSTEM\enwiktionary-latest-stub-articles\stub-articles.xml';
  8.   tf := 'A:1.txt';
  9.   SS := TFileStream.Create(sf, fmOpenRead);
  10.   ST := TFileStream.Create(tf, fmCreate or fmOpenWrite);
  11.   try
  12.     SS.Read(Buffer, sizeof(Buffer));
  13.     Buffer := stringreplace(Buffer, '<page>','<p>', [rfReplaceAll]);
  14.     Buffer := stringreplace(Buffer, '</page>','</p>', [rfReplaceAll]);
  15.     Buffer := stringreplace(Buffer, '<title>','<t>', [rfReplaceAll]);
  16.     Buffer := stringreplace(Buffer, '</title>','</t>', [rfReplaceAll]);
  17.     Buffer := stringreplace(Buffer, '<ns>','<n', [rfReplaceAll]);
  18.     Buffer := stringreplace(Buffer, '</ns>','>', [rfReplaceAll]);
  19.     Buffer := stringreplace(Buffer, '<revision>','<r>', [rfReplaceAll]);
  20.     Buffer := stringreplace(Buffer, '</revision>','</r>', [rfReplaceAll]);
  21.     Buffer := stringreplace(Buffer, '<id>','<i', [rfReplaceAll]);
  22.     Buffer := stringreplace(Buffer, '</id>','>', [rfReplaceAll]);
  23.     Buffer := stringreplace(Buffer, '<parentid>','<pi', [rfReplaceAll]);
  24.     Buffer := stringreplace(Buffer, '</parentid>','>', [rfReplaceAll]);
  25.     Buffer := stringreplace(Buffer, '<contributor>','', [rfReplaceAll]);
  26.     Buffer := stringreplace(Buffer, '</contributor>','', [rfReplaceAll]);
  27.     Buffer := stringreplace(Buffer, '<username>','<u>', [rfReplaceAll]);
  28.     Buffer := stringreplace(Buffer, '</username>','</u>', [rfReplaceAll]);
  29.     Buffer := stringreplace(Buffer, '<comment>','<c>', [rfReplaceAll]);
  30.     Buffer := stringreplace(Buffer, '</comment>','</c>', [rfReplaceAll]);
  31.     Buffer := stringreplace(Buffer, '<text id="','<t =', [rfReplaceAll]);
  32.     Buffer := stringreplace(Buffer, '" bytes="',' b=', [rfReplaceAll]);
  33.     ST.Write(Buffer, sizeof(Buffer));
  34.   finally
  35.     SS.Free;
  36.   end;
  37.  
Jen se ještě pokouším o záměny v textu a nevede se mi. Nedělá to to co jsem očekával, vzhledem k tomu, že ještě měním ten původní text, tak zbytek místa v bloku je prázdný.

100406

  • Host
Re:Jak načíst část souboru?
« Odpověď #3 kdy: 30-06-2018, 15:58:57 »
Tak Ty jsi ten s tou bibli ;D Aha, no delej jak chces (lobovat tu za efektivni reseni nebudu). Jinak na ten zbytek zkusim po fotbale mrkout ;)
« Poslední změna: 30-06-2018, 16:02:16 od 100406 »

Offline vangog

  • Hrdina
  • ****
  • Příspěvků: 422
  • Karma: 0
    • Verze Delphi: 7
Re:Jak načíst část souboru?
« Odpověď #4 kdy: 30-06-2018, 16:09:05 »
A na to si přišel jak? Každopádně mi jde o nějaké jednoúčelové vyexportování dat, takže efektivita mi na srdci neleží, ale je fakt, že soubor co chci extrahovat je velký jak prase.

100409

  • Host
Re:Jak načíst část souboru?
« Odpověď #5 kdy: 30-06-2018, 16:44:52 »
A na to si přišel jak? Každopádně mi jde o nějaké jednoúčelové vyexportování dat, takže efektivita mi na srdci neleží, ale je fakt, že soubor co chci extrahovat je velký jak prase.

Jednoduse ;)

Offline raul

  • Hrdina
  • ****
  • Příspěvků: 468
  • Karma: 15
    • Verze Delphi: FPC :D
Re:Jak načíst část souboru?
« Odpověď #6 kdy: 30-06-2018, 16:50:34 »
Tvuj priklad nebude fungovat. Co kdyz nactes do bufferu .....<us a v dalsi varce ername>... ?? Hmm :) Aneb jak se to delat nema..
Lazarus 1.6.3:), FPC, Intel/Arm, Windows/Linux, (občas Delphi)

100411

  • Host
Re:Jak načíst část souboru?
« Odpověď #7 kdy: 30-06-2018, 16:53:25 »
Tvuj priklad nebude fungovat. Co kdyz nactes do bufferu .....<us a v dalsi varce ername>... ?? Hmm :) Aneb jak se to delat nema..

I tohle ma reseni ;)

Offline raul

  • Hrdina
  • ****
  • Příspěvků: 468
  • Karma: 15
    • Verze Delphi: FPC :D
Re:Jak načíst část souboru?
« Odpověď #8 kdy: 30-06-2018, 16:56:39 »
Rikal nekdo, ze nema ?
Lazarus 1.6.3:), FPC, Intel/Arm, Windows/Linux, (občas Delphi)

100413

  • Host
Re:Jak načíst část souboru?
« Odpověď #9 kdy: 30-06-2018, 18:26:20 »
Rikal nekdo, ze nema ?

Ne. Ale pripomina mi to sceny ze zivota. Napr. zenske jsou schopny vyrazit s tvrzenim "delas to spatne" bez rozumneho navrhu jak "to tedy delat spravne". Je treba se jich navic zeptat "jak to delat spravne" ;)

Resenim pro tak velky soubor je pouzit memory mapped file a cist jej v blocich s tim ze pokud narazis "pred koncem obsahu bloku" na oteviraci tag "<", a ctes dalsi bloky dokud v obsahu nasledujiciho bloku nenarazis na tag uzaviraci ">".

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3534
  • Karma: 139
    • Verze Delphi: D2007, XE3, DX10
Re:Jak načíst část souboru?
« Odpověď #10 kdy: 30-06-2018, 18:44:21 »
Jestli je to validni XML dokument, tak bych to vubec neprasil jako text, ale prohnal to nejakym SAX like parserem

Offline miroB

  • Guru
  • *****
  • Příspěvků: 655
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005 .. D Tokyo 10.2.3 Pro C/S, D12.2
Re:Jak načíst část souboru?
« Odpověď #11 kdy: 30-06-2018, 20:33:52 »
Rikal nekdo, ze nema ?
..
Resenim pro tak velky soubor je pouzit memory mapped file a cist jej v blocich s tim ze pokud narazis "pred koncem obsahu bloku" na oteviraci tag "<", a ctes dalsi bloky dokud v obsahu nasledujiciho bloku nenarazis na tag uzaviraci ">".
Aha.. A to by ako vyzeralo? Nejaka mala ukazka v Delphi? To by ma teda zaujimalo. S podobnym problemom som sa viackrat stretol. Najlepsie rovno v tom UTF8?

Offline vangog

  • Hrdina
  • ****
  • Příspěvků: 422
  • Karma: 0
    • Verze Delphi: 7
Re:Jak načíst část souboru?
« Odpověď #12 kdy: 30-06-2018, 20:46:06 »
Musel by to být parser který není náročný na množství použité RAM.
Co to udělat pomocí readln?

Nějaké ukázky ale bez editace jsem našel tady:
https://stackoverflow.com/questions/455790/fast-read-write-from-file-in-delphi
« Poslední změna: 30-06-2018, 21:15:19 od vangog »

Offline miroB

  • Guru
  • *****
  • Příspěvků: 655
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005 .. D Tokyo 10.2.3 Pro C/S, D12.2
Re:Jak načíst část souboru?
« Odpověď #13 kdy: 30-06-2018, 20:51:18 »
Čo sa týka XML Parseru, tak používam tento: http://www.destructor.de/xmlparser/download.htm
Má demo, ako použiť. Veľmi sa mi páči. Nepotrebuje nahrať naraz celý súbor. A je rýchly.
Niečo ako malý SAX.
Musel by to být parser který není náročný na množství použité RAM.
Co to udělat pomocí readln?
Neviem, ako by readln pomohlo, aby som zachytil všetky tagy neprerušené.

Offline vangog

  • Hrdina
  • ****
  • Příspěvků: 422
  • Karma: 0
    • Verze Delphi: 7
Re:Jak načíst část souboru?
« Odpověď #14 kdy: 30-06-2018, 21:39:10 »
No jo, ale xml parser soubor jenom rozdělí, ale nijak nezmenší velikost celkových dat.

Offline miroB

  • Guru
  • *****
  • Příspěvků: 655
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005 .. D Tokyo 10.2.3 Pro C/S, D12.2
Re:Jak načíst část souboru?
« Odpověď #15 kdy: 30-06-2018, 21:52:27 »
No jo, ale xml parser soubor jenom rozdělí, ale nijak nezmenší velikost celkových dat.
Odpoved vidim tu.. Myslim treba pockat nez Delfin dopozera fotbal.. :)
..
Resenim pro tak velky soubor je pouzit memory mapped file a cist jej v blocich s tim ze pokud narazis "pred koncem obsahu bloku" na oteviraci tag "<", a ctes dalsi bloky dokud v obsahu nasledujiciho bloku nenarazis na tag uzaviraci ">".

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3534
  • Karma: 139
    • Verze Delphi: D2007, XE3, DX10
Re:Jak načíst část souboru?
« Odpověď #16 kdy: 30-06-2018, 23:07:40 »
No jo, ale xml parser soubor jenom rozdělí, ale nijak nezmenší velikost celkových dat.
No vsak: parser ti udela lexikalni analyzu a je na tobe, co udelas s jednotlivymi elementy. Jinak pokud to mermomoci chces prasit jako text, tak v podstate vsechny lexikalni analyzatory mivaji implementovany nejakou podobu unget(), aby se mohli vracet na zacatek naposledy zkoumaneho prvku a treba si fyzicky docist dalsi data.

A nebo to vubec neprogramovat v Delphi a zkusit XSLT tranformaci ve spojeni se SAX.

Offline vangog

  • Hrdina
  • ****
  • Příspěvků: 422
  • Karma: 0
    • Verze Delphi: 7
Re:Jak načíst část souboru?
« Odpověď #17 kdy: 30-06-2018, 23:20:40 »
Zatím jsem se dostal k tomuto:

Kód: Delphi [Vybrat]
  1.     ifname,ofname:string;
  2.     sourceStream, targetStream: TFileStream;
  3.     filesizevalue, size:integer;
  4.     Buffer: AnsiString;
  5. begin
  6.   ifname := 'U:\SYSTEM\enwiktionary-latest-stub-articles\stub-articles.xml';
  7.   ofname := 'A:1.txt';
  8.   filesizevalue:=900000;
  9.   size := 1;
  10.   SetLength(Buffer, filesizevalue);
  11.   sourceStream := TFileStream.Create(ifname, fmOpenRead);
  12.   targetStream := TFileStream.Create(ofname, fmCreate or fmOpenWrite);
  13.   try
  14.     sourceStream.seek(0, soFromBeginning);
  15.     sourceStream.ReadBuffer(PAnsiChar(Buffer)^, filesizevalue * size);
  16.  
  17.     buffer := stringreplace(buffer, '<page>','<p>', [rfReplaceAll]);
  18.     buffer := stringreplace(buffer, '</page>','</p>', [rfReplaceAll]);
  19.     buffer := stringreplace(buffer, '<title>','<t>', [rfReplaceAll]);
  20.     buffer := stringreplace(buffer, '</title>','</t>', [rfReplaceAll]);
  21.     buffer := stringreplace(buffer, '<ns>','<n', [rfReplaceAll]);
  22.     buffer := stringreplace(buffer, '</ns>','>', [rfReplaceAll]);
  23.     buffer := stringreplace(buffer, '<revision>','<r>', [rfReplaceAll]);
  24.     buffer := stringreplace(buffer, '</revision>','</r>', [rfReplaceAll]);
  25.     buffer := stringreplace(buffer, '<id>','<i', [rfReplaceAll]);
  26.     buffer := stringreplace(buffer, '</id>','>', [rfReplaceAll]);
  27.     buffer := stringreplace(buffer, '<parentid>','<pi', [rfReplaceAll]);
  28.     buffer := stringreplace(buffer, '</parentid>','>', [rfReplaceAll]);
  29.     buffer := stringreplace(buffer, '<contributor>','', [rfReplaceAll]);
  30.     buffer := stringreplace(buffer, '</contributor>','', [rfReplaceAll]);
  31.     buffer := stringreplace(buffer, '<username>','<u>', [rfReplaceAll]);
  32.     buffer := stringreplace(buffer, '</username>','</u>', [rfReplaceAll]);
  33.     buffer := stringreplace(buffer, '<comment>','<c>', [rfReplaceAll]);
  34.     buffer := stringreplace(buffer, '</comment>','</c>', [rfReplaceAll]);
  35.     buffer := stringreplace(buffer, '<text id="','<t =', [rfReplaceAll]);
  36.     buffer := stringreplace(buffer, '" bytes="',' b=', [rfReplaceAll]);
  37.     targetStream.Write(PAnsiChar(buffer)^, length(buffer) );
  38.   finally
  39.      sourceStream.free;
  40.      targetStream.free;
  41.   end;
  42.  

Ovšem chtělo by to vyřešit problém s výkonem, stringreplace je zde příliš pomalý.  Nejspíš to zkusím udělat pomocí DIRegEx 8.8.1 . Třída DIRegEx.TDIRegEx má funkce Replace a Replace2 pomocí kterých by to mělo jít taky.

Offline miroB

  • Guru
  • *****
  • Příspěvků: 655
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005 .. D Tokyo 10.2.3 Pro C/S, D12.2
Re:Jak načíst část souboru?
« Odpověď #18 kdy: 30-06-2018, 23:35:51 »
Mozne varianty <pages   ..  /> .. ?
« Poslední změna: 30-06-2018, 23:39:11 od Miroslav Baláž »

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3534
  • Karma: 139
    • Verze Delphi: D2007, XE3, DX10
Re:Jak načíst část souboru?
« Odpověď #19 kdy: 30-06-2018, 23:44:45 »
Excellent
Rated 1 time
Ovšem chtělo by to vyřešit problém s výkonem, stringreplace je zde příliš pomalý.  Nejspíš to zkusím udělat pomocí DIRegEx 8.8.1 . Třída DIRegEx.TDIRegEx má funkce Replace a Replace2 pomocí kterých by to mělo jít taky.
Ty bys mel je pointerem pres buffer a porovnavat znak na '<'. Zapamatovat si jeho pozici, opsat text pred nim na vystup a hledat znak '>'. Pokud ho nenajdes do konce bufferu, tak presunout text od zapamatovane pozice '<' do konce bufferu na zacatek bufferu a za nej docist zbytek bufferu ze souboru.

A kdyz budes mit pointery na pocatecni a koncove znacky, tak budes porovnavat obsah mezi znackami s polozkami mapovaci mapovaci tabulky - pres pointery, zadne tahani kotat do stringu apod. Nic rychlejsiho neudelas.

Offline vangog

  • Hrdina
  • ****
  • Příspěvků: 422
  • Karma: 0
    • Verze Delphi: 7
Re:Jak načíst část souboru?
« Odpověď #20 kdy: 01-07-2018, 00:37:24 »
Tak nějak mě to taky napadlo, jen je to už dost nízkoúrovňové. Zkouším TDIRegEx replace. Jen nemohu přijít na to jak zapsat ten výsledek do buffer.

Kód: Delphi [Vybrat]
  1. sourceStream.ReadBuffer(PAnsiChar(Buffer)^, filesizevalue * size);
  2. RegEx.SetSubjectStr(buffer);
  3. RegEx.MatchPattern := '(*UCP)<([ptrc])(age|itle|evision|omment)>';
  4. RegEx.FormatPattern := '<$1>';
  5. RegEx.Replace(0);
  6.  

Nevypadá to že by to provedlo změnu na buffer.

Offline miroB

  • Guru
  • *****
  • Příspěvků: 655
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005 .. D Tokyo 10.2.3 Pro C/S, D12.2
Re:Jak načíst část souboru?
« Odpověď #21 kdy: 01-07-2018, 01:30:16 »
Tak nějak mě to taky napadlo, jen je to už dost nízkoúrovňové. Zkouším TDIRegEx replace. Jen nemohu přijít na to jak zapsat ten výsledek do buffer.
..
Asi je potom najjednoduchsie vyuzit grepwin https://sourceforge.net/projects/grepwin/
Primarne je na hladanie, ale je tam aj moznost:
1. replace
2. vyuzitie regex
Asi by to mohlo fungovat
« Poslední změna: 01-07-2018, 01:32:33 od Miroslav Baláž »

Offline vangog

  • Hrdina
  • ****
  • Příspěvků: 422
  • Karma: 0
    • Verze Delphi: 7
Re:Jak načíst část souboru?
« Odpověď #22 kdy: 01-07-2018, 02:15:04 »
Už mi to funguje. Je třeba použít replace2(buffer):

Kód: Delphi [Vybrat]
  1.     RegEx.SetSubjectStr(buffer);
  2.     RegEx.MatchPattern := '(*UCP)<(/)?([ptrcu])(age|itle|evision|omment|sername)>';
  3.     RegEx.FormatPattern := '<$1$2>';
  4.     RegEx.Replace2(buffer);
  5.  
  6.     RegEx.SetSubjectStr(buffer);
  7.     RegEx.MatchPattern := '(*UCP)<(ns)>(\d+)</ns>|<(id)>(\d+)</id>|<(parentid)>(\d+)</parentid>';
  8.     RegEx.FormatPattern := '<$1$3$5*$2$4$6>';
  9.     RegEx.Replace2(buffer);
  10.  
  11.     RegEx.SetSubjectStr(buffer);
  12.     RegEx.MatchPattern := '(*UCP)</?contributor>';
  13.     RegEx.FormatPattern := '';
  14.     RegEx.Replace2(buffer);
  15.  
  16.     RegEx.SetSubjectStr(buffer);
  17.     RegEx.MatchPattern := '(*UCP)<(?:restrictions|timestamp|sha1|format)>(?U).*</(?:restrictions|timestamp|sha1|format)>';
  18.     RegEx.FormatPattern := '';
  19.     RegEx.Replace2(buffer);
  20.  
  21.  
  22.     RegEx.SetSubjectStr(buffer);
  23.     RegEx.MatchPattern := '(*UCP)(id|bytes)="';
  24.     RegEx.FormatPattern := '$1*';
  25.     RegEx.Replace2(buffer);
  26.  
  27.     RegEx.SetSubjectStr(buffer);
  28.     RegEx.MatchPattern := '(*UCP)Wiktionary|(?<c>)Remove withtext= from |Lang section formatting \(|Undo revision |Reverted edits by |Removing interwiki links \|Getting_rid_of_interwikis|discussion';
  29.     RegEx.FormatPattern := '';
  30.     RegEx.Replace2(buffer);
  31.  

Výkon je nesrovnatelně lepší oproti stringreplace. Soubor se zmenší cca o polovinu, ještě to chce odstranit mezery.

Offline vangog

  • Hrdina
  • ****
  • Příspěvků: 422
  • Karma: 0
    • Verze Delphi: 7
Re:Jak načíst část souboru?
« Odpověď #23 kdy: 14-08-2020, 13:07:45 »
V souvislosti s regulárními výrazy a (a touto knihovnou DIRegEx) mám dotaz. Když provedu záměnu tagů na zkrácené značky:

Kód: Delphi [Vybrat]
  1. source[1] := StringReplace(source[1],'<span class="greek2">', '<G>', [rfReplaceAll]);
  2. source[1] := StringReplace(source[1],'<span class="hebrew">', '<H>', [rfReplaceAll]);
  3. source[1] := StringReplace(source[1],'<span class="manuref">', '<M>', [rfReplaceAll]);
  4. source[1] := StringReplace(source[1],'<span class="abbreviation">', '<AB>', [rfReplaceAll]);
  5. source[1] := StringReplace(source[1],'<span class="accented">', '<AC>', [rfReplaceAll]);
  6. source[1] := StringReplace(source[1],'<span class="topic">', '<T>', [rfReplaceAll]);
  7. source[1] := StringReplace(source[1],'<span class="textheading">', '<LI>', [rfReplaceAll]);
  8.  

Dal by se použít nějaký inteligentní regulerní výraz pro nahrazování, který by nahradil i ty uzavírající tagy?
Např. '<span class="topic">' je <LI> a uzavírací tag je <LI>. Po nahrazení pomocí StringReplace tedy mám <LI>1.</span> místo <LI>1.</LI>. Šlo by to pomocí regexu zaměnit všechny tagy najednou nebo musím definovat 5. samostatných regulárních předloh? Protože takových situací mám více a znamenalo by to dost velké množství kódu a méně efektivní nahrazování.