Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[C++] Wczytywanie pliku z buforowaniem

Ostatnio zmodyfikowano 2024-04-03 20:33
Autor Wiadomość
idepozapalki
Temat założony przez niniejszego użytkownika
[C++] Wczytywanie pliku z buforowaniem
» 2024-04-02 23:40:48
Dzień dobry,
Zainspirowany trochę kursem chciałem poskładać cegiełki i użyć elementów do wczytywania dużych plików z buforowaniem z użyciem elementów z bibliotek.

Mój zamysł jest taki żeby otworzyć plik przy użyciu ifstream, następnie wczytywanie go po kawałku do stringstream i w końcu pobraniu linii przy użyciu getline.

Moja piaskownica w której sobie testuje i wyciągam jakieś wnioski:

C/C++
#include <fstream>
#include <iostream>
#include <sstream>

class file1 {
public:
   
int filesize;
   
int chunksize = 2048;
   
std::ifstream file;
   
std::stringstream ss;
   
std::streamsize s;
   
   
file1() {
       
file.open( "test.txt", std::ios::binary | std::ios::ate );
       
filesize = file.tellg();
       
file.seekg( 0, std::ios::beg ); // rewind to the beginning
       
std::cout << filesize << " Bytes" << '\n';
       
std::cout << "Koniec!\n";
   
}
   
   
void ladujbufor() {
       
char * buffer = new char[ chunksize ];
       
file.read( buffer, chunksize ); // czytaj z pliku blok znakow
       
ss.write( buffer, chunksize ); // zapisz blok do stringstreamu
       
delete[ ] buffer;
       
std::streamsize a = file.gcount(); // pokazuje jaka porcje wczytalo do bufora z pliku, moze sie przydac przy ostatniej mniejszej porcji
   
}
   
   
   
std::string getLine() {
       
std::string line;
       
std::getline( ss, line );
       
//s = ss.gcount();
       
int sssize = ss.tellg();
       
return line;
   
}
   
   
void czysc() {
       
int sssize = ss.tellg();
       
ss.ignore( sssize );
       
sssize = ss.tellg();
   
}
   
   
~file1() {
       
file.close();
   
}
}
;


int main() {
   
   
file1 f1;
   
f1.ladujbufor();
   
f1.ladujbufor();
   
std::cout << f1.getLine() << '\n';
   
f1.czysc()
   
f1.getLine();
   
f1.ladujbufor();
}

Oczywiście jest to wersja w najprostszej postaci bez obsługi błędów i uproszczona do bólu.
Po co to robię? Bo samo wczytanie pliku tekstowego do bufora w całości zajmuje u mnie ponad minutę i jest to czas w którym właściwie nic poza tym się nie dzieje. Dlatego użyłem tego to read i write.
To co sprawia mi problemy to właściwie 2 rzeczy:
1. metoda ladujbufor() powoduje za każdym wywołaniem dołożenie do stringstream ss kolejnego fragmentu z pliku.
   Widać to w debugerze podglądając zawartość ss. Chciałbym w jakiś sposób wyczyścić metodą czyść() początek stringstramu z którego pobrałem linię metodą getLine(), bo dokładanie kolejnych fragmentów metodą ladujbufor() powoduje, że ss "puchnie".
2. Używanie getLine() niesie ze sobą niebezpieczeństwo że w którymś momencie dojdę do końca bufora i nie znajdę końca linii. Czy da się coś z tym zrobić? Zachować ten kawałek bez \r\n

Oczywiście przeglądałem net i takiego gotowca nie znalazłem.
Czy takie podejście ma sens czy muszę to rozwiązanie uzbroić w dodatkowe elementy, że zadziałało.
P-180967
DejaVu
» 2024-04-03 09:36:33
Chat GPT:
Dzień dobry! Twój pomysł na buforowanie danych podczas czytania dużych plików jest bardzo sensowny, szczególnie gdy mamy do czynienia z sytuacjami, w których odczyt całego pliku naraz jest nieefektywny lub niemożliwy z powodu ograniczeń pamięciowych. Twoje podejście do rozwiązania problemu jest na dobrym kierunku, ale pozwolę sobie na kilka sugestii, które mogą pomóc Ci w udoskonaleniu kodu.

### Optymalizacja metody `ladujbufor()`

Metoda `ladujbufor()` jest kluczową częścią Twojego rozwiązania, ale wydaje się, że nie bierzesz pod uwagę przypadku, gdy odczytane z pliku dane są mniejsze niż `chunksize`. To może być problematyczne, zwłaszcza przy ostatnim fragmencie pliku. Możesz to naprawić, korzystając z `file.gcount()` do zapisania tylko rzeczywistej liczby przeczytanych bajtów do `stringstream`:

```cpp
void ladujbufor() {
    char * buffer = new char[chunksize];
    file.read(buffer, chunksize); // czytaj z pliku blok znakow
    std::streamsize bytesRead = file.gcount(); // rzeczywista liczba przeczytanych bajtów
    ss.write(buffer, bytesRead); // zapisz tylko przeczytane bajty
    delete[] buffer;
}
```

### Czyszczenie `stringstream`

Rozumiem, że chcesz "wyczyścić" `stringstream` z danych, które już zostały przetworzone. Jednak sposób, w jaki próbujesz to zrobić (`ss.ignore(sssize);`), nie działa tak, jak oczekujesz. `stringstream` nie jest zaprojektowany do "usuwanie" danych z początku strumienia w łatwy sposób. Jednym z rozwiązań jest przechowywanie nieprzetworzonych danych w innym `stringstream` po każdym odczycie linii:

```cpp
void przepiszNieprzetworzone() {
    std::string reszta;
    std::getline(ss, reszta, '\0'); // Pobierz wszystko do końca jako jedną linię
    ss.str(""); // Wyczyść strumień
    ss.clear(); // Resetuj stan strumienia
    ss << reszta; // Wstaw resztę danych z powrotem do strumienia
}
```

### Obsługa końca linii w buforze

Rozwiązanie problemu z końcem linii, który może nie pojawić się w buforze, wymaga bardziej złożonego podejścia. Jednym ze sposobów jest przechowywanie niezakończonej linii między wywołaniami `ladujbufor()`, a następnie łączenie jej z następną porcją danych, aby zapewnić ciągłość. Można to osiągnąć przez dodanie zmiennej członkowskiej klasy, która będzie przechowywać niezakończoną linię, i modyfikację `getLine()` oraz `ladujbufor()`, aby obsługiwały ten przypadek.

### Podsumowanie

Twoje podejście ma sens, ale wymaga kilku modyfikacji, aby poprawnie obsługiwać przypadki brzegowe i usprawnić zarządzanie pamięcią. Zastosowanie się do powyższych sugestii powinno pomóc w osiągnięciu celu efektywniejszego czytania dużych plików. Jeśli masz jakiekolwiek pytania lub potrzebujesz dalszej pomocy, daj znać!
P-180968
idepozapalki
Temat założony przez niniejszego użytkownika
» 2024-04-03 20:33:39
Dziękuję bardzo, za podpowiedź.
Nawet nie brałem tego pod uwagę.
Bardzo podoba mi się ten kierunek.
P-180969
« 1 »
  Strona 1 z 1