2010-10-07 14 views
5

std::istream ma prototyp istream& read (char* s, streamsize n)rzeczywisty liczba odczytanych bajtów należy zdobyć dzwoniąc istream::gcount(), również ważność istream może być znany z ios::good.Zrozumienie projektowania std :: istream :: czytaj

Omawiałem implementację innej klasy strumieniowej, którą próbowałem napisać z moim kolegą, gdzie mówiłem, że mogę podążać za tym projektem; ale powiedział, że zamiast wywoływać użytkownika za każdym razem, można mieć prototyp odczytywania, taki jak ten istream& read (char* s, streamsize n, size_t &bytes_read), tak że przejdzie on w jednym wywołaniu, a pierwszy jest bardziej niezgrabny. Nie byłem w stanie obronić wyboru projektu w stylu std. Jakie jest prawdziwe uzasadnienie dla istream::read?

+0

Masz na myśli 'size_t & bytes_written'? I prawdopodobnie powinien to być 'streamed & bytes_written' (lub może' chars_read'). –

+0

@ James: Tak, dziękuję! Bajty i znaki są synonimami _tutaj, ponieważ 'sizeof' jest taki sam :) – legends2k

Odpowiedz

4

Zakładam, że to dlatego, że C++ zazwyczaj nie wymusza interfejsu, który może nie być potrzebny dla wszystkich. Jeśli potrzebujesz read, aby zaakceptować parametr, na który niektórzy ludzie nie dbają, to powoduje to dodatkową pracę kodowania (deklarowanie dodatkowej int do przekazania jako parametru). Zawsze zapisuje również bajty odczytane niezależnie od tego, czy klient ma to na uwadze czy nie (niektórzy klienci mogą po prostu uważać, że odczyt nie powiódł się, jak wskazują bity eof/fail).

Za pomocą oddzielnej metody można rozdzielić interfejs na różne informacje, które mogą ale nie muszą być potrzebne.

0

std::istream ma prototyp istream& read (char* s, streamsize n) rzeczywisty liczba bajtów czytać należy zdobyć dzwoniąc istream::gcount(), również ważność z istream może być znana z ios::good.

istream::read(char* s, streamsize n) odczytuje niesformatowanego bloku pamięci (bez NULL rozwiązania) wielkości n elementów tablicy s. Mimo że s jest wskaźnikiem do char, można użyć istream::read do odczytywania danych binarnych. Na przykład, można mieć istream, który przechowuje wartości tablicy podwaja (zakładając, że kolejność bajtów jest poprawna):

unsigned int count; 
input.read(reinterpret_cast<char*>(&count), sizeof(count)); 
double* data = new double[count]; 

for (unsigned int i = 0; i < count; ++i) 
    input.read(reinterpret_cast<char*>(data[i]), sizeof(double)); 

istream::gcount() zwraca liczbę bajtów odczytanych w ostatnim istream::read rozmowy. W tym przypadku widzimy, że rozmiar count jest prawdopodobnie inny niż rozmiar double, dlatego nie moglibyśmy użyć istream::gcount() do określenia rozmiaru pierwszego elementu w tablicy data.

+0

@CashCow Ma poprawne rozwiązanie dla ograniczeń' istream :: read'. Nie mogę przegłosować, ponieważ nie mam co najmniej 15 punktów reputacji (nadal jestem Stack Overflow n00b). –

+0

Moje aktualne pytanie brzmiało: dlaczego mamy wywoływać 'gcount'; zamiast tego, dlaczego nie ma to takiego, jak wywołujący może przekazać zmienną refernce, która zostanie ustawiona przez 'odczyt' na liczbę faktycznie odczytanych bajtów (która może być mniejsza lub równa' streamize n'), zamiast wywoływania gcount każdego razu. – legends2k

+0

Wydaje się, że lepiej jest zwrócić liczbę bajtów odczytanych jako @CashCow wyjaśnionych przez 'istream :: readsome'. Jak wyjaśnił @ Mark B, unika się konieczności deklarowania dodatkowej int do przekazania przez odniesienie. Inną opcją jest przekazanie wskaźnika do zmiennej, która jest domyślnie ustawiona na 'NULL', której można użyć do zignorowania faktycznej liczby przeczytanych bajtów:' istream & read (char * s, stream n, stream * bajtRead = NULL) '. Jednak nie wiem, czy zaoferuje znaczną przewagę nad 'istream :: readsome'. –

2

Spróbuj readsome polecenia Zamiast

streamsize readsome (char* buf, streamsize num); 

buf jest twój bufor i num to liczba bajtów, które chcą czytać, co najwyżej liczbę bajtów dostępnych w buforze, oczywiście.

Wartość zwracana to liczba faktycznie odczytanych bajtów.

Aby odczytać plik do końca można pętla:

char buf[BUF_SIZE] 
streamsize bytesRead; 
do 
{ 
    bytesRead = instr.readsome(buf, BUF_SIZE); 
    // do stuff with the bytes you read, if any 
} while (bytesRead == BUF_SIZE); 
0

W odpowiedzi na oryginalne pytanie, mające połączenia wyboru błąd był popularny styl programowania kiedy C był młody, ale wyszedł z mody wkrótce po. To, co się dzieje, to małe rzeczy, które nie są bardzo złe, ale mimo to prawie zawsze są tylko trochę gorsze, przez jakiś czas, dopóki nie zostaną wywołane i oznaczone jako złe przez społeczność. Ten kod ma nieszczęście, że został napisany, zanim ten mały anty-wzorzec był szeroko dyskutowany.

W odpowiedzi na rozwiązanie Cash Cow, myślę, że jest błąd. Jeśli czekasz na IO i masz wystarczająco dużo znaków, aby częściowo wypełnić bufor, funkcja powróci i pętla while zakończy się, zanim plik zostanie całkowicie odczytany. Więc jego rozwiązanie prawdopodobnie działałoby poprawnie, gdyby było napisane na prostym, surowym IO, ale nie przeszło przez buforowane IO.

Prawidłowym rozwiązaniem będzie oczywiście zakończenie pętli while po ustawieniu flagi EOF. Nie jestem w tej chwili pewny, jaka jest najlepsza reakcja, gdy ustawi się badbit, ale prawdopodobnie powinieneś również zająć się tą sprawą.

Zgadzam się, że readsome to przyzwoita alternatywa dla czytania.

Edytuj: Czasami nie jest dostępny plik Readsome (niektóre wersje VC++). W takim przypadku odczyt nie jest bezużyteczny.