Čtení velkého souboru – C / C++ – Fórum – Programujte.com
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu

Čtení velkého souboru – C / C++ – Fórum – Programujte.comČtení velkého souboru – C / C++ – Fórum – Programujte.com

 

Toto vlákno bylo označeno za vyřešené — příspěvek s řešením.
richard.zavodny0
Návštěvník
22. 12. 2016   #1
-
0
-

Ahoj, já vím, že opět otravuju, ale mám další problém. Čtu soubory a převádím je do Base64 kódování. To už mám naprogramované a kupodivu je to i funkční, ale nyní jsem narazil na další problém. Čtení velkých souborů (od stovek MB). Vždycky mi ten program spadne, jakmile se pokusím ten soubor přečíst a já nevím proč, respektive nevím jak to vyřešit a přečíst ho co nejméně náročně (Ať už na prostředky počítače, tak i časově).

Tady je kód:  

#include <fstream>
 
int main() {
    fstream fileInput;
    ofstream fileOutput;
    string encodedFileContent;
 
    fileInput.open("./file.mp4");
    fileOutput.open("./file.base64txt");
 
    string fileContent((istreambuf_iterator<char>(fileInput)), istreambuf_iterator<char>());
    encodedFileContent = base64Encode(reinterpret_cast<const unsigned char*> (fileContent.c_str()), fileContent.length());
    fileOutput << encodedFileContent;
 
    fileInput.close();
    fileOutput.close();

    return 0;
}


Díky za radu. :)

Nahlásit jako SPAM
IP: 109.81.208.–
Programátor, Kóder, Grafický designér, Kritik, zastánce Open-Source a Linuxu.
gna
~ Anonymní uživatel
1891 příspěvků
22. 12. 2016   #2
-
0
-

Tak ten soubor nenačítej celý najednou, ale po částech.

Nahlásit jako SPAM
IP: 213.211.51.–
richard.zavodny0
Návštěvník
22. 12. 2016   #3
-
0
-

#2 gna
To je, ale ten problém, že nemám zdání jak na to. :( Googlil jsem, vyhodilo mi to desítky diskuzí na Stackoverflow, ale nic nefunguje. :/

Nahlásit jako SPAM
IP: 109.81.208.–
Programátor, Kóder, Grafický designér, Kritik, zastánce Open-Source a Linuxu.
gna
~ Anonymní uživatel
1891 příspěvků
22. 12. 2016   #4
-
0
-
Nahlásit jako SPAM
IP: 213.211.51.–
KIIV
~ Moderátor
+43
God of flame
22. 12. 2016   #5
-
0
-

I ten nejblbejsi debuger dokaze rici proc to pada. Pouzij nejaky.

A pokud nepouzivas c++11 ci novejsi, tak se tam dela docela dost kopii, a potreboval bys docela hodne pameti.

Nahlásit jako SPAM
IP: 93.91.151.–
Program vždy dělá to co naprogramujete, ne to co chcete...
richard.zavodny0
Návštěvník
22. 12. 2016   #6
-
0
-

#5 KIIV
Hází mi to tento error: "terminate called after throwing an instance of 'std::bad_alloc'what():  std::bad_alloc Aborted (core dumped)". Pokud dobře čtu, tak je problém s alokací paměti.(?)

Tuto chybu mi to vyhodilo u každého příkladu, který jsem našel na Stackoverflow (tedy, když mi ho kompilátor "sežral").

Můj kód:  

#include <fstream>
 
int main() {
    ifstream fileInput;
    ofstream fileOutput;
    string fileInputName;
    string fileOutputName;

    fileInput.open("./file.mp4");
    fileOutput.open("./file.base64txt");

    if(fileInput) {
        fileInput.seekg(0, fileInput.end);
        int fileInputSize = fileInput.tellg();
        fileInput.seekg(0, fileInput.beg);
        char * fileContent = new char[fileInputSize];
        string encodedFileContent;

        cout << fileInputSize << "\n";
        fileInput.read(fileContent, fileInputSize);
        encodedFileContent = base64Encode(reinterpret_cast<const unsigned char*> (fileContent), fileInputSize);
        fileOutput << encodedFileContent;

        delete[] fileContent;
    }

    fileInput.close();
    fileOutput.close();
}


Už se s tím "crcám" možná tři dny a nejsem schopný přijít na funkční řešení. :/

Nahlásit jako SPAM
IP: 109.81.208.–
Programátor, Kóder, Grafický designér, Kritik, zastánce Open-Source a Linuxu.
KIIV
~ Moderátor
+43
God of flame
22. 12. 2016   #7
-
0
-

#6 richard.zavodny
Ano, bad alloc je obvykle nedostatek pameti.

Neco takoveho musis enkodovat po castech. Blok muzes udelat velky po nasobcich 6 (tedy alespon tipuju, ze by to tak melo jit).

Nahlásit jako SPAM
IP: 93.91.151.–
Program vždy dělá to co naprogramujete, ne to co chcete...
Staon0
Návštěvník
23. 12. 2016   #8
-
0
-

#6 richard.zavodny
Přesně tak, jak KIIV píše - došla vám paměť. Soubor máte zkopírovaný v paměti dvakrát - jednou načtený a jednou enkódovaný - a ještě tam budete mít nějaký overhead způsobený nafukováním stringů při načítání.

Skutečně musíte načítat po menších částech. Stačí bloky o násobku 3, protože base64 překódovává 3 B na 4 B.

Protože ale bufferování do paměti řeší už C++ streamy, je poměrně zbytečné jejich funkci duplikovat. Takže, podle mne, ještě elegantnější řešení je napsat kódování base64 jako automat (má tři stavy: 1. B, 2. B, 3. B, a pak přechází zpět do stavu 1. B a přitom zapíše překódované 4 B do výstupu) a v cyklu číst byte po byte ze vstupního streamu a předávat je do automatu, který to zase průběžně zapisuje do výstupní streamu. Takhle to dělám u nás v práci. Mám to naimplementované jako C++ stream, takže použití je pak úplně triviální.

Nahlásit jako SPAM
IP: 94.142.234.–
richard.zavodny0
Návštěvník
23. 12. 2016   #9
-
0
-

Omlouvám se za pozdější reakci, avšak zkouším všechno možné a výsledku se ne, a ne dobrat.. :/ Já momentálně potřebuju:

  1. Přečíst soubor po třech bajtech (sakra velký problém)
  2. Obsah třech bajtů uložit do proměnné (neměl by být problém)
  3. Proměnou předhodit funkci, která kóduje data do Base64 (bez problému)
  4. Zakódována data uložit na konec výstupního souboru (bez problému)


Je mi jasné (tedy předpokládám :D), že budu muset použít cyklus while, avšak nevím jakým způsobem. Už si opravdu připadám jako blb, takže kdyby byl někdo tak hodný a těch pár řádků kódu mi napsal (jelikož ani Google nepomohl), byl bych moc rád. :)

Předem díky. :)

Nahlásit jako SPAM
IP: 109.81.208.–
Programátor, Kóder, Grafický designér, Kritik, zastánce Open-Source a Linuxu.
richard.zavodny0
Návštěvník
24. 12. 2016   #10
-
0
-

#7 KIIV
No, takže jsem to zkusil ještě takhle (kód níže), ale toto mi vypíše první tři znaky a jejich zakódovanou verzi a potom to akorát vyhodí chybu Segmentation fault (core dumped).

Můj kód:  

#include <fstream>
 
int main() {
    ifstream fileInput;
    ofstream fileOutput;
    string fileInputName;
    string fileOutputName;
    char * fileContent = new char[3];
    string encodedFileContent;

    fileInputName = directoryName + "/" + entryName;
    fileOutputName = directoryName + "/" + entryName + ".cxenc";

    fileInput.open(fileInputName.c_str());
    fileOutput.open(fileOutputName.c_str());

    while(!fileInput.fail()) {
        fileInput.read(fileContent, 3);
        cout << fileContent << " - ";

        encodedFileContent = base64Encode(reinterpret_cast<const unsigned char*> (fileContent), 3);

        cout << encodedFileContent << "\n";

        fileContent = ""; //Pokud sem dam delete[] fileContent;, tak to vyhodi jeste silenejsi chybu..
    }

    fileInput.close();
    fileOutput.close();
}


Už jsem fakt bezradný a pomalu tluču hlavou o zeď, že jsem se na to C++ vůbec dal.. Měl jsem zůstat u Javy... :D

Nahlásit jako SPAM
IP: 109.81.208.–
Programátor, Kóder, Grafický designér, Kritik, zastánce Open-Source a Linuxu.
Staon0
Návštěvník
24. 12. 2016   #11
-
0
-

#10 richard.zavodny

Váš program je samozřejmě špatně, protože si po průběhu prvního cyklu přenastavíte ukazatel na buffer fileContent na literál "", který je ovšem uložený v paměti, která je pouze pro čtení. Proto segment violation. Čištění fileContent na konci cyklu úplně zrušte, protože následující read vám jeho obsah stejně přepíše.

Mimochodem i řádka

      cout << fileContent << " - ";

je špatně, protože vyžaduje, aby obsah fileContent byl ukončený nulou. Takže pokud vám to funguje, tak pouze náhodou, že zrovna za bufferem v paměti je nula.

Na konci programu vám chybí delete [] fileContent; aby se uvolnila alokovaná paměť. Osobně bych vám doporučil buffer o 3 znacích nealokovat na haldě, ale jednoduše ho udělat rovnou na zásobníku: 

char fileContent[3];

C předává pole do funkcí jako ukazatel na první prvek, takže proměnnou fileContent můžete rovnou dát jako argument funkce read i jako argument funkce base64Encode (tady se ale možná bude mlátit char a unsigned char a bude potřeba přetypování).

Anebo, když jsou ty Vánoce, tak tady je řešení, které jsem navrhoval já: 

#include <assert.h>
#include <iostream>
#include <stdint.h>

namespace {

const char base64_table[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

} /* -- namespace */

class Encoder {
  private:
    uint8_t ochunk[4];
    int oindex;

  public:
    Encoder();
    
    void encode(
        uint8_t byte_,
        std::ostream& os_);
};

Encoder::Encoder() :
  oindex(0) {

}

void Encoder::encode(
    uint8_t byte_,
    std::ostream& os_) {
  switch(oindex) {
    case 0:
      ochunk[3] = (byte_ & 0xfc) >> 2;
      ochunk[2] = (byte_ & 0x3) << 4;
      break;
    case 1:
      ochunk[2] |= (byte_ & 0xf0) >> 4;
      ochunk[1] = (byte_ & 0xf) << 2;
      break;
    case 2:
      ochunk[1] |= (byte_ & 0xc0) >> 6;
      ochunk[0] = (byte_ & 0x3f);

      /* -- write data */
      os_.put(base64_table[ochunk[3]]);
      os_.put(base64_table[ochunk[2]]);
      os_.put(base64_table[ochunk[1]]);
      os_.put(base64_table[ochunk[0]]);

      break;
    default:
      assert(false);
      break;
  }

  oindex = (oindex + 1) % 3;
}

int main(
    int argc_,
    char* argv_[]) {
  Encoder encoder_;
  int c_;
  while((c_ = std::cin.get()) != std::char_traits<char>::eof()) {
    encoder_.encode(static_cast<uint8_t>(c_), std::cout);
  }
  return 0;
}

Je potřeba ještě dopsat funkci close, která vám zarovná výstup na konci - pokud vstup není násobek 3, tak se do výstupu přidávají zarovnávací znaky =. (Mimochodem, tohle neřeší ani váš program.) A samozřejmě by to chtělo ošetřit chyby. Ale to, doufám, případně už zvládnete sám.

Nahlásit jako SPAM
IP: 194.149.122.–
Řešení
KIIV
~ Moderátor
+43
God of flame
24. 12. 2016   #12
-
0
-
Vyřešeno Nejlepší odpověď

#11 Staon
Akorat mi to nacitani skoncilo uz po par kilobytech u 700MB mkv.

#10 richard.zavodny
Jinak kopirovani po 30000B trvalo asi 16s (jen kopirovani):

    char buffer[30000];

    do {
        in.read(buffer, sizeof(buffer));
        std::streamsize size = in.gcount();
        if (size > 0) {
            out.write(buffer, size);
        }
    } while(in);

S enkodovanim to trva jeste o trosku dele (cca 23s s -O2):

    char buffer[30000];
    do {
        in.read(buffer, sizeof(buffer));
        std::streamsize size = in.gcount();
        if (size > 0) {
            out << base64_encode((unsigned char *)buffer, size);
        }
    } while (in);

in je vstupni soubor a out je vystupni soubor

Nahlásit jako SPAM
IP: 93.91.151.–
Program vždy dělá to co naprogramujete, ne to co chcete...
Staon0
Návštěvník
27. 12. 2016   #13
-
0
-

#12 KIIV
Máte na mysli původní kód, nebo ten můj? Pokud to druhé, tak tam asi mám nějakou chybu, ale nevím jakou, protože tim bez problému překóduji 900MB avi.

Nahlásit jako SPAM
IP: 94.142.234.–
KIIV
~ Moderátor
+43
God of flame
27. 12. 2016   #14
-
0
-

#13 Staon
uz to mam davno smazane, ale kdo vi, treba jsem v tom fofru udelal chybu ja a nacital to do charu nebo tak neco. Se pripadne omlouvam za takovou blbost

EDIT: tak to opravdu funguje. Sice cista kopie trvala 56s na tom samem, co po blocich trva 16s, ale ani rychlost neni uplne strasna.

Nahlásit jako SPAM
IP: 94.113.99.–
Program vždy dělá to co naprogramujete, ne to co chcete...
richard.zavodny0
Návštěvník
28. 12. 2016   #15
-
0
-

Díky, moc, už to jede jak má. :)

Nahlásit jako SPAM
IP: 109.81.208.–
Programátor, Kóder, Grafický designér, Kritik, zastánce Open-Source a Linuxu.
Zjistit počet nových příspěvků

Přidej příspěvek

Toto téma je starší jak čtvrt roku – přidej svůj příspěvek jen tehdy, máš-li k tématu opravdu co říct!

Ano, opravdu chci reagovat → zobrazí formulář pro přidání příspěvku

×Vložení zdrojáku

×Vložení obrázku

Vložit URL obrázku Vybrat obrázek na disku
Vlož URL adresu obrázku:
Klikni a vyber obrázek z počítače:

×Vložení videa

Aktuálně jsou podporována videa ze serverů YouTube, Vimeo a Dailymotion.
×
 
Podporujeme Gravatara.
Zadej URL adresu Avatara (40 x 40 px) nebo emailovou adresu pro použití Gravatara.
Email nikam neukládáme, po získání Gravatara je zahozen.
-
Pravidla pro psaní příspěvků, používej diakritiku. ENTER pro nový odstavec, SHIFT + ENTER pro nový řádek.
Sledovat nové příspěvky (pouze pro přihlášené)
Sleduj vlákno a v případě přidání nového příspěvku o tom budeš vědět mezi prvními.
Reaguješ na příspěvek:

Uživatelé prohlížející si toto vlákno

Uživatelé on-line: 0 registrovaných, 33 hostů

Podobná vlákna

Cteni souboru. — založil ewzen

Čtení souboru ve VB — založil hansk

Moderátoři diskuze

 

Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032024 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý