Ответ 1
Поток представляет собой последовательность объектов (обычно байтов, но необязательно), к которым можно получить доступ в последовательном порядке. Типичные операции над потоком:
- прочитайте один байт. В следующий раз, когда вы прочтете, вы получите следующий байт и т.д.
- прочитайте несколько байтов из потока в массив
- искать (перемещать текущую позицию в потоке, так что в следующий раз, когда вы читаете, вы получите байты из новой позиции)
- написать один байт
- записать несколько байтов из массива в поток
- пропустить байты из потока (это похоже на чтение, но вы игнорируете данные. Или если вы предпочитаете его искать, но можете только переходить вперед.)
- отбросить байты во входной поток (это как "отменить" для чтения - вы засунули несколько байтов в резервную копию потока, так что в следующий раз вы прочтете это, что вы увидите. Это иногда полезно для парсеров, так как является:
- peek (смотрите байты, не читая их, чтобы они все еще присутствовали в потоке, который нужно прочитать позже)
Конкретный поток может поддерживать чтение (в этом случае это "входной поток" ), запись ( "выходной поток" ) или и то, и другое. Не все потоки доступны для поиска.
Откат назад довольно редок, но вы всегда можете добавить его в поток, обернув реальный поток ввода в другом потоке ввода, который содержит внутренний буфер. Считывается из буфера, и если вы нажмете назад, данные будут помещены в буфер. Если в буфере нет ничего, тогда поток обратного потока читается из реального потока. Это простой пример "адаптера потока": он находится на "конце" входного потока, это сам входной поток, и он делает что-то дополнительное, что исходный поток не сделал.
Stream - полезная абстракция, потому что он может описывать файлы (которые действительно являются массивами, следовательно, поиск является простым), но также терминальный ввод/вывод (который не доступен для поиска, если не буферизирован), сокеты, последовательные порты и т.д. Таким образом, вы можете написать код, в котором говорится: "Я хочу некоторые данные, и мне все равно, откуда и от чего это получилось", или "Я создам некоторые данные, и это полностью зависит от моего вызывающего, что с ним происходит". Первый принимает параметр входного потока, последний принимает параметр выходного потока.
Лучшая аналогия, о которой я могу думать, это то, что поток - это конвейерный ленточный конвейер, идущий к вам или ведущий от вас (или иногда оба). Вы извлекаете материал из потока ввода, вы помещаете материал в выходной поток. Некоторые конвейеры, о которых вы можете думать, выходят из отверстия в стене - они не доступны для поиска, чтение или письмо - это одноразовая сделка. Некоторые конвейеры выложены перед вами, и вы можете двигаться по выбору места нахождения в потоке, который хотите читать/писать, - в поиске.
Как утверждает IRBMe, лучше всего думать о потоке с точки зрения операций, которые он предлагает (которые варьируются от реализации до реализации, но имеют много общего), а не по физической аналогии. Потоки - это "вещи, которые вы можете читать или писать". Когда вы начинаете подключать потоковые адаптеры, вы можете думать о них как о коробке с конвейером и конвейером, которую вы подключаете к другим потокам, а затем ящик выполняет некоторые преобразования данных (застегивая его или изменяя строки строк в UNIX к DOS, или что-то еще). Трубы - еще один тщательный тест метафоры: там, где вы создаете пару потоков, чтобы все, что вы пишете в одном, можно было читать из другого. Подумайте о червоточинах: -)