2009-10-09 11 views
7

Próbowałem wypisać tablicę char zakończoną znakiem null do pliku.C++ zakończone znakiem null wyprowadzenie tablicy char znaków

Rzeczywiście, otrzymuję pakiety, a następnie drukuję ich pola.

Teraz, gdy te pola nie są zakończone wartością NULL, na przykład segment danych ma rozmiar 512, ale może być lub może nie być całkowicie zajęty.

Kiedy zapisuję te dane do pliku, używam prostej przeciążonej funkcji < <, która nie wie nic na temat rzeczywistych danych i szuka tylko zakończenia segmentu danych.

Więc, jak mogę powiedzieć funkcji wyjściowej, aby napisać tylko tyle bajtów ?

Zamiast coś takiego co jest drogie, aby zadzwonić za każdym razem:

enter code here 

bytescopied = strncpy(dest, src, maxbytes); 

if (bytescopied < 0) { // indicates no bytes copied, parameter error 

    throw(fit);   // error handler stuff here 

} else if (bytescopied == maxbytes) { 

    dest[maxbytes-1] = '\0'; // force null terminator 

} 

Odpowiedz

16

Jeśli chcesz umieścić dokładnie maxbytes bajtów, należy write metoda

stream.write(buffer, maxbytes); 

Jeśli masz mniej bajtów w buforze, jak wiesz, ilu z nich swoją bufor zawiera? Jeśli '\0' znaki buffer koniec, można napisać:

stream.write(buffer, std::find(buffer, buffer+maxbytes, '\0') - buffer); 
+0

W drugim wywołaniu cały drugi parametr jest okrężnym sposobem wywoływania zwykłego starego 'strlenka '. Ale w takim przypadku możesz równie dobrze użyć bardziej idiomatycznego sposobu dla całej instrukcji: 'stream << buffer'. –

+0

Nie. Strlen nie zatrzymuje się po maxbytes. –

+0

'strnlen' będzie. Wywołanie strnlen będzie wyglądało znacznie czystsze: "strnlen (buffer, maxbytes)". Żadnych miauczeń za pomocą wskaźników lub czegokolwiek. – gnud

3

Tani rozwiązaniem byłoby mieć bufor, który ma miejsce na dodatkowy znak null i po prostu umieścić znak null u wskaż, kiedy znasz faktyczny rozmiar, a następnie wyprowadź bufor zakończony znakiem null, tak jak już zrobiłeś. Szybki i niezawodny.

+0

Tak, można zapisać ten dodatkowy znak w zmiennej tymczasowej. Po zakończeniu drukowania po prostu go przywróć. –

3

To działa, ale nie jest bezpieczny przed przypadkowym wywołaniem standardowego char* wersję operator<<:

#include <iostream> 

template <unsigned N> 
std::ostream& operator<< (std::ostream& out, const char (& data) [N]) 
{ 
    out.write (data, N); 
    // or out.write (data, strnlen (data, N)); 
    // if you want to stop at a '\0' in the data 
    return out; 
} 


struct Foo { 
    char one[5]; 
    char two[1]; 
    char three[5]; 
}; 

int main (void) 
{ 
    using namespace std; 

    Foo foo = { 
     { 'h', 'e', 'l', 'l', 'o' }, 
     { ' ' }, 
     {'w', 'o', 'r', 'l', 'd'} }; 

    cout << foo.one; 
    cout << foo.two; 
    cout << foo.three; 
    cout << endl; 
} 

Jest to bezpieczniejsze, stosując typ maxw co ogranicza długość następnej produkcji :

struct maxw { 
    unsigned n; 
    maxw (unsigned n) : n (n) { } 
}; 

struct maxw_stream { 
    std::ostream& stream; 
    unsigned n; 
    maxw_stream (std::ostream& stream, unsigned n) : 
      stream (stream), 
      n (n) { 
    } 
}; 

maxw_stream operator<< (std::ostream& out, const maxw& m) 
{ 
    return maxw_stream (out, m.n); 
} 

std::ostream& operator<< (const maxw_stream& out, const char* data) 
{ 
    out.stream.write (data, strnlen (data, out.n)); 
    return out.stream; 
} 

// eg: 
cout << maxw(4) << "Hello World!" << endl; 
// Hell\n 
cout << maxw(100) << "Hello World!" << endl; 
// Hello World!\n 
+0

Problem z strnlen jest taki, że AFAIK nie jest standardem. I twoja funkcja szablonu jest dobra, ale jeśli bufor jest przekazywany jako zwykły wskaźnik, a nie tablica, nie będzie żadnego ostrzeżenia, tylko standardowa funkcja wyjściowa c-string zostanie wywołana. –

+0

Jest w C99 od dziesięciu lat, a IIRC C++ 0x wprowadza wszystkie standardy C99. –

+0

Cóż, szukałem http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1124.pdf i nie mogłem znaleźć strnlen. –

0

Widzę głównie dwa soluti ons.

W przypadku danych ASCII:

memset(dest,0,destlength); 
bytescopied = strncpy(dest, src, maxbytes); 

Wtedy będziesz zawsze mieć jasną NUL łańcuch w buffor.

Po drugie w przypadku danych ASCII:

std::string yourASCII(src,maxbytes); 
yourASCII.c_str() // would be null terminated. 
0

Jeśli nie dbają o ostatnim bajcie, można po prostu

buffer[buffersize-1] = 0; 

a następnie karmić bufor cokolwiek funkcja ciąg chcesz. Jeśli jest krótszy, wszystko zostanie uruchomione do terminatora o wartości zerowej , który już istnieje, a jeśli nie było terminatora, uruchomi się do właśnie utworzonego.

I to szybko :)

Powiązane problemy