[C++] Najszybszy algorytm odczytywania z pliku


(Lol600000065) #1

Siema,

chciałbym się zapytać czy poniższy algorytm do odczytywania zawartości pliku jest dobry. A może jest jakaś funkcja biblioteczna np. z windows.h lepsza od niego :?:

Odczytywanie zawartosci pliku do stringu:

ifstream stru("Plik.txt");

(Fiołek) #2

Plik najlepiej odczytywać kawałkami(np. po kilka/kilkaset kB) lub cały od razu(ale tutaj pewności nie mam, czy to jest dobre).

i/_ofstream_ to w sumie tylko nakładka na natywne metody systemu Windows(CreateFile, Read/_WriteFile_ itp.)

Zapisz do pliku znaki o kodach ASCII/UTF(zależy jakiego kodowania używasz) które nie są do zrozumienia przez człowieka i będziesz miał "krzaczki" :wink:


([alex]) #3

Ten algorytm jest najgorszy z możliwych. Po wczytaniu każdego znaku zwiększa się rozmiar napisu "zawartosc". To oznacza przydzielanie nowego większego bufora, kopiowanie do niego starej zawartości, dopisanie znaku, zwolnienie starej pamięci. Jak już musisz wczytać całą zawartość to:

  • [*:2som99td]odczytaj długość pliku

Nie zapomnij otworzyć plik w trybie binarnym inaczej długość nie będzie się zgadzać.


(Lol600000065) #4

Do pliku na początku zapisywałem (a raczej cały czas są zapisywane) same stringi w trybie niebinarnym. Można potem taki plik czytać binarnie ? (Pytam bo co chwilę mi się mylą te pojęcia - np. stringi zapisywane w trybie binarnym da się odczytać... a przecież powinny być tam krzaczki wynikające z przekształcenia wszystkich znaków ASCII na kod dwójkowy)...


([alex]) #5

Owszem, będą krzaczki, z tym że potem można to skonwertować.


(Lol600000065) #6

Źle mnie chyba zrozumiałeś.

Napisałem, że właśnie nie będzie żadnych krzaczków przy stringach przy zapisie binarnym. Będą dopiero jak dodatkowo dopiszemy jakąś zmienną (co jest dziwne - dlaczego nie mogą być krzaczki bez dopisywania zmiennej :?: ).

W każdym razie może zadam inaczej pytanie - jaki algorytm tu zastosować, żeby plik dało się odczytać też bezpośrednio po kliknięciu - a nie za pomocą programu (więc chyba tryb binarny odpada, bo będą krzaczki (tzn. w Visualu nie ma, ale to pewnie defekt tego kompilatora)). Może taki:

1) dowiaduje się jaką długość ma plik (przesuwam wskaźnik czytania na koniec i wywołuję tellg())

2) robię resize stringu na taką długość

3) kopiuję potem zawartość: ob_string = stru_wej.get()

Może nie najlepszy ale już nie trzeba za każdym razem alokować nowego miejsca na string. Co Ty na to [alex] :?:


(Fiołek) #7

Bo tekst zapisany binarnie i nie-binarnie zawsze będzie zbiorem znaków...

Tekstu nie da się zapisać jako "krzaczki". Krzaczki to znaki spoza ASCII lub znaki bez odpowiedników graficznych(np. znak ASCII o kodzie 0, 1, 2).


([alex]) #8

Odczytać można jedną instrukcją:

zawartosc.resize(dlugosc);stru_wej.read(const_castchar*(zawartosc.c_str()),dlugosc); [/code]
EDIT: poprawiono, byla taka sama nazwa strumienia i zmiennej typu string.

(Lol600000065) #9


(Fiołek) #10

Liczba w komputerze zawsze będzie zapisana w postaci dwójkowej bo komputer inaczej tego nie zapisze.

Jeśli zapisujesz jakąś "zmienną" nie tekstową w trybie nie-binarnym zostanie ona najpierw skonwertowana na tekst a dopiero potem ten tekst zapisany. W trybie binarnym nie ma tej konwersji tylko strumień zapisze tą zmienną. Dla przykładu:

int txt = 6513249; //Jakby nie patrzeć to "zmienna typu nie-tekstowego"

char* cstr = (char*)&txt; //Ale to już jest "zmienna" tekstowa(to jest c-string)

Jeśli zapiszesz binarnie txt to otrzymasz(odczytując plik tekstowo) dokładnie to co się kryje pod zmienną cstr.


([alex]) #11

Rzeczywiście przekombinowałem próbując zachować twoje nazewnictwo, poprawiono w poście wyżej,

resize() i c_str() odnoszą się do zmiennej typu string.

zawartosc.resize(dlugosc);stru_wej.read(const_castchar*(zawartosc.c_str()),dlugosc); [/code]
Co do rzutowania to w C++ to jedyny sposób rzutowania z const char* na char*, owszem w C było można inaczej.

Co do odczytania pliku to nawet jak jest zapisany nie binarnie, to zawsze możesz odczytać go binarnie (zamiast \n będzie \r\n), na dodatek to jest jedyna droga aby przydzielić dokładnie tyle znaków (może o jeden więcej na znak końca napisu) ile jest w pliku.

Można też wg twojego wariantu, z tym że:
[code=php]ifstream stru_wej("Plik.txt",ios::binary);stru_wej.seekg(0,ios::end);ios::pos_type dlugosc=stru_wej.tellg();stru_wej.seekg(0,ios::beg);char* zawartosc=new char[dlugosc+1];stru_wej.get(zawartosc,dlugosc+1);zawartosc[dlugosc]=0;cout  zawartosc;delete zawartosc; Z tym że ze stringiem trochę prościej wychodzi.

Nie zapominaj też o konwersji z Win1250 na Latin2, plik prawdopodobnie jest w kodowaniu Win1250 zaś to co wyświetlasz na ekran w trybie konsolowym powinno być w Latin2.

(Lol600000065) #12

Aha sorry, bo ja źle zrozumiałem (myślałem, że chodzi o char* na const char*, bo do tego konwersji nie potrzeba). Ale przecież są te rzutowania starego stylu:

nowy_typ(wyrazenie) (nowy_typ)wyrazenie[/code]

I niektórzy w C++ dalej ich używają, ale TYLKO (z tego co ja się spotkałem) przy tych funkcjach właśnie nieformatowanych. Dlaczego ? Nie chce im się pisać dłuższej formy const_cast ?

[quote]
":3j5xz562](zamiast \n będzie \r\n)
[/quote]


Na odwrót ;)

[quote]
":3j5xz562]to jest jedyna droga aby przydzielić dokładnie tyle znaków (może o jeden więcej na znak końca napisu) ile jest w pliku.
[/quote]


Jak otworzyłem plik nie-binarnie to funkcja get i tak działała. Ponadto, ona nie wczyta do tablicy tyle znaków ile napisaliśmy ([i]dlugosc[/i]) tylko [i]dlugosc-1[/i] bo dodaje jeszcze nulla potem, więc trzeba napisać [i]dlugosc+1[/i] (i znowu Comeau C++ Online uważa, że to jest dobrze, a VS, że powinno być [i]dlugosc+1L[/i] - nie wiem czego, bo ta klasa szablonowa fpos nic mi nie mówi) tak więc mój kod jest OK (zapomniałem tylko skasować tablicę delete)

[quote]
":3j5xz562]Z tym że ze stringiem trochę prościej wychodzi.
[/quote]


I na pewno nie naruszy się żadna pamięć, albo coś popsuje jak użyjemy const_cast'u wobec c_str() ? Chyba twórcy tej biblioteki nie na zarty dali zwrot funkcji typu CONST. Jestes pewny, ze nic sie nie stanie :?:

[quote]
":3j5xz562]Nie zapominaj też o konwersji z Win1250 na Latin2, plik prawdopodobnie jest w kodowaniu Win1250 zaś to co wyświetlasz na ekran w trybie konsolowym powinno być w Latin2.
[/quote]


Pierwszy raz słyszę o tych pojęciach. Rzucilem okiem na wiki i tam jest napisane ze obydwa kodowania mają polskie znaki, więc o co chodzi :?:

[b]Fiołek[/b], cały czas chodzi mi o to, że komputer prędzej czy później i tak te wszystkie C-stringi, stringi zapisze jako kod dwójkowy - nikt w pamięci tekstów nie trzyma. Więc co za problem zrobić to już w pliku z trybem binarnym...

(Fiołek) #13

Notatnik woli AFAIK CP-1250, ale tylko on. I konsola domyślnie kodowana jest nie w Latin2, tylko CP-852(cmd->chcp).

Ale to nie są takie same kodowania. Sprawdź

Eee? W komputerze WSZYSTKO to liczby(zapisane na bitach, czyli w kodzie dwójkowym) więc nie wiem o co Ci chodzi :wink:

Co do trzymania w pamięci - ja trzymam jak mi potrzeba, ale dysk to też jest pamięć :wink:


(Lol600000065) #14

No właśnie. Tak naprawdę string czy C-string składa się z pojedynczych znaków ASCII, znak ASCII to jakaś liczba, a liczbę zapisuje się za pomocą kodu binarnego. Więc dlaczego w trybie binary - czyli kiedy kompilator zapisuje binarnie wszystko do pliku - wyjątek stanowią teksty, które da się odczytać - tak się już przyjęło ? Prościej nie mogę wytłumaczyć o co mi chodzi.


(Fiołek) #15

Nie tak się przyjęło tylko taka jest kolej rzeczy. Zauważ, że edytor tekstu lub inny program nie odczytuje pojedynczych bitów tylko bajty(a każdy znak ASCII zmieści się na jednym bajcie) i je wyświetla.


([alex]) #16

(Fiołek) #17

Może i są takie same(nie sprawdzałem - nie chce mi się), ale to nadal nie są takie same kodowania.

Mówimy tutaj o ASCII a nie o tych kodowaniach, dlatego uprościłem.


(Lol600000065) #18

A to to, że trzeba zrobić odpowiedni resize to wiem oczywiscie...

Sorry, pomieszało mi się, myślałem, że get() nie ma domniemanego '\n', tzn. jezeli wybierzemy funkcje 2-arg. to nie będzie zadnego ogranicznika. Wybrałem więc w końcu funkcję read, i zrobię to ze stringiem, tak jak mówisz [alex].

ifstream stru_wej("Plik.txt");stru_wej.seekg(0, ios::end);ios::pos_type dlugosc = stru_wej.tellg();stru_wej.seekg(0, ios::beg);

([alex]) #19

Zapomniałeś otworzyć w trybie binarnym. Ten kod może mieć różne śmieci na końcu napisu "zawartosc".


(Lol600000065) #20

A co da zapis binarny tutaj :?: On coś robi jeszcze poza CRLF :?: