2015-05-21 4 views
2

Исходя из C, я немного экспериментирую с C++ и наткнулся на что-то столь же простое, как чтение двоичных данных из файла в буфер с использованием ifstream. Мне кажется, что у меня есть три варианта для чтения данных из файла:Потоковые бинарные файлы в C++

  • get(), который получает один символ, который кажется странным и неэффективными для чтения большой части данных в буфер памяти;
  • read(), который не возвращает, сколько символов он действительно читает; и
  • readsome(), который, если я правильно его понимаю, возвращает только ранее буферизованные данные, но не считывает ничего нового из фактического файла.

Что поражает меня как особенно странно это read() функция, которая, мне кажется, совершенно непригодным для видя, как он не говорит, сколько байтов он фактически помещается в буфер. Тем не менее, весь пример кода, который я вижу, использует, кажется, проверяет, что это так, и обычно ищет конец файла для извлечения размера файла и последующего выделения буфера. Очевидно, что это не работает для потоковой передачи данных.

Итак, как же на самом деле предполагается поток файла/pipe/socket с нетекстовыми данными на C++? Может быть, есть лучшее средство, чем ifstream?

ответ

5

Что мне кажется особенно странным, так это функция read(), которая кажется мне совершенно непригодной для использования, поскольку она не показывает, сколько байтов она фактически помещает в поставляемый буфер.

read() не выходит, пока не 1) запрошенное количество символов не было прочитано, 2) достигнуто EOF или 3) произошла ошибка.

После того, как read() выходы, если чтение было успешным, вы можете позвонить gcount(), чтобы узнать, сколько символов было прочитано в буфере. Если во время чтения было достигнуто EOF, состояние потока eofbit будет установлено в true, и gcount() вернет меньше символов, чем вы просили.

Если сбой чтения, состояние потока failbit и/или badbit равно true.

std::ifstream ifs(...); 
if (is) { 
    // stream opened... 
    //... 
    ifs.read(buffer, sizeof(buffer)); 
    if (ifs) { 
     // read succeeded, EOF may have been reached... 
     std::streamsize numInBuf = ifs.gcount(); 
     //... 
    } else { 
     // read failed... 
    } 
    //... 
} else { 
    // stream not opened... 
} 

Если вы используете exceptions() метод потока, чтобы включить отчеты об ошибках через исключение, std::ios_base::failure исключения может быть выброшено, если отказ соответствует ошибочным битам вы включили исключения для.

std::ifstream ifs; 
ifs.exceptions(std::ifstream::badbit | std::ifstream::failbit); 
try { 
    ifs.open(...); 
    // stream opened... 
    //... 
    ifs.read(buffer, sizeof(buffer)); 
    // read succeeded, EOF may have been reached... 
    std::streamsize numInBuf = ifs.gcount(); 
    //... 
} catch (const std::ios_base::failure &e) { 
    // stream failure... 
} 

Так как один на самом деле должен течь файл/трубы/штуцер с нетекстовыми данными в C++? Может быть, есть лучшее средство, чем ifstream?

std::ifstream предназначен для файловых потоков. В случае с трубами, если ваша платформа может обращаться к трубам через стандартные файловые API, std::ifstream должен работать. Для сокетов, однако, вам необходимо использовать более подходящий класс std::basic_istream или, по крайней мере, использовать стандартный std::istream с специальным классом std::streambuf, прикрепленным к нему (example).

+0

Ах, 'gcount()' похоже, что я пропал без вести. Благодаря! – Dolda2000

0

«Что поражает меня как особенно странно это read() функция, которая, мне кажется, совершенно непригодным для видя, как он не говорит, сколько байтов он фактически помещается в буфер.»

Ну, вы указываете, сколько байт может быть прочитан как максимум за один раз, и может проверить, если поток уже исчерпан после использования std::ifstream::eof() для проверки состояния.

Если вы хотите знать, сколько байтов уже доступны из файла, и выделить ваш буфер чтения, соответственно, вы можете проверить весь размер файла заранее, как упоминалось, с небольшими изменениями этого образца кода из the reference:

// read entire file into string 
std::ifstream is("test.txt", std::ifstream::binary); 
if (is) { 
    // get length of file: 
    is.seekg(0, is.end); 
    int length = is.tellg(); 
    is.seekg(0, is.beg); 

    std::string str; 
    str.resize(length, ' '); // ******* reserve space ******** 
    char* begin = &*str.begin(); 

    is.read(begin, length); 
    is.close(); 

    std::cout << str << "\n"; 
} else { 
    std::cout << "Could not open test.txt\n"; 
} 

Обратите внимание, что решающим моментом для вашего вопроса является то, что вы можете полностью выделить length за один раз или вам нужно разделить чтение небольших фрагментов и разбор их последовательно, когда вы не можете предоставить буфер, который является способный хранить полный размер файла.

"Очевидно, что это не работает для потоковых данных, однако."

Это просто вопрос о расцепления полученных блоков данных из асинхронной задачи на более высоком уровне нити потребляя данные в потокового образом, как бы вы хотите.

+0

'is.seekg (0, is.end);' - Это не сработает, например. трубы или розетки. – Dolda2000

+0

@ Dolda2000 Ну, вы должны уточнить эту озабоченность в своем вопросе. Я бы предпочел просто использовать ['read()'] (http://linux.die.net/man/2/read) и ['poll()'] (http://linux.die.net/ man/3/poll), чтобы читать из любых входных потоков. –

+0

Ну, вот почему я говорил о «потоке». Я предполагал, что это будет достаточно ясно. Тем более, что я специально возражал против этого конкретного дела в вопросе. :) – Dolda2000

Смежные вопросы