Co to jest strumień?

Odpowiedzi:

171

Strumień reprezentuje sekwencję obiektów (zwykle bajtów, ale niekoniecznie), do których można uzyskać dostęp w kolejności sekwencyjnej. Typowe operacje na strumieniu:

  • czytaj jeden bajt. Następnym razem, gdy będziesz czytać, otrzymasz następny bajt i tak dalej.
  • odczytaj kilka bajtów ze strumienia do tablicy
  • seek (przesuń swoją aktualną pozycję w strumieniu, aby następnym razem, gdy będziesz czytać, otrzymasz bajty z nowej pozycji)
  • napisz jeden bajt
  • zapisz kilka bajtów z tablicy do strumienia
  • pomiń bajty ze strumienia (to jest jak odczyt, ale ignorujesz dane. Lub, jeśli wolisz, to jest jak wyszukiwanie, ale możesz iść tylko do przodu).
  • wepchnij bajty z powrotem do strumienia wejściowego (to jest jak „cofnij” do odczytu - wsuwasz kilka bajtów z powrotem do strumienia, więc następnym razem zobaczysz to, co zobaczysz. Czasami jest to przydatne dla parserów, tak jak:
  • podgląd (spójrz na bajty bez ich czytania, aby nadal znajdowały się w strumieniu i można je było przeczytać później)

Określony strumień może obsługiwać odczyt (w takim przypadku jest to „strumień wejściowy”), zapis („strumień wyjściowy”) lub oba. Nie wszystkie strumienie można przeszukiwać.

Odrzucenie jest dość rzadkie, ale zawsze można je dodać do strumienia, opakowując rzeczywisty strumień wejściowy w inny strumień wejściowy, który przechowuje wewnętrzny bufor. Odczyty pochodzą z bufora, a jeśli wrócisz, dane są umieszczane w buforze. Jeśli w buforze nie ma nic, strumień wypychający odczytuje strumień rzeczywisty. To jest prosty przykład „adaptera strumienia”: znajduje się on na „końcu” strumienia wejściowego, sam jest strumieniem wejściowym i robi coś więcej niż oryginalny strumień.

Strumień jest użyteczną abstrakcją, ponieważ może opisywać pliki (które tak naprawdę są tablicami, stąd szukanie jest proste), ale także wejścia / wyjścia terminala (których nie można przeszukiwać, chyba że są buforowane), gniazda, porty szeregowe itp. Możesz więc pisać kod, który mówi albo „Potrzebuję pewnych danych i nie obchodzi mnie, skąd one pochodzą ani w jaki sposób się tu dostały” albo „Stworzę pewne dane i wszystko zależy od dzwoniącego, co się z nimi stanie”. Pierwsza przyjmuje parametr strumienia wejściowego, druga przyjmuje parametr strumienia wyjściowego.

Najlepsza analogia, jaką przychodzi mi do głowy, jest taka, że ​​strumień jest pasem transmisyjnym zbliżającym się do ciebie lub odchodzącym od ciebie (lub czasami jednym i drugim). Pobierasz rzeczy ze strumienia wejściowego, umieszczasz je w strumieniu wyjściowym. Niektóre przenośniki, o których możesz pomyśleć, jako wychodzące z dziury w ścianie - nie można ich szukać, czytanie lub pisanie to jednorazowa okazja. Niektóre przenośniki są rozłożone przed tobą i możesz poruszać się, wybierając miejsce w strumieniu, które chcesz czytać / pisać - to jest wyszukiwanie.

Jak jednak mówi IRBMe, najlepiej jest myśleć o strumieniu pod kątem oferowanych przez niego operacji (które różnią się w zależności od implementacji, ale mają wiele wspólnego), a nie przez analogię fizyczną. Strumienie to „rzeczy, które możesz czytać lub pisać”. Kiedy zaczynasz podłączać adaptery strumieniowe, możesz myśleć o nich jako o pudełku z przenośnikiem wchodzącym i wychodzącym, które łączysz z innymi strumieniami, a następnie pudełko wykonuje pewną transformację danych (spakowanie ich lub zmiana doprowadzeń linii UNIX do systemów DOS lub cokolwiek). Potoki to kolejny dokładny test metafory: w tym miejscu tworzysz parę strumieni, tak że wszystko, co napiszesz w jednym, może być odczytane z drugiego. Pomyśl o tunelach czasoprzestrzennych :-)

Steve Jessop
źródło
3
Zdecydowanie najlepsze wyjaśnienie, jakie przeczytałem. W połączeniu z tym, co jest napisane w SICP („Przetwarzanie strumieniowe pozwala nam modelować systemy, które mają stan bez użycia przypisania lub modyfikowalnych danych”), myślę, że w końcu to rozumiem. Dziękuję Ci!
Kyle Chadha
92

Strumień jest już metaforą, analogią, więc naprawdę nie ma potrzeby przedstawiania kolejnego. Możesz myśleć o tym w zasadzie jako o rurze z przepływem wody, w której woda jest w rzeczywistości danymi, a rura jest strumieniem. Przypuszczam, że jest to rodzaj dwukierunkowej rury, jeśli strumień jest dwukierunkowy. Zasadniczo jest to powszechna abstrakcja, która jest umieszczana na rzeczach, w których istnieje przepływ lub sekwencja danych w jednym lub obu kierunkach.

W językach takich jak C #, VB.Net, C ++, Java itp. Metafora strumienia jest używana do wielu celów. Istnieją strumienie plików, w których otwierasz plik i możesz czytać ze strumienia lub zapisywać w nim w sposób ciągły; Istnieją strumienie sieciowe, w których czytanie zi zapisywanie do strumienia odczytuje i zapisuje do bazowego ustanowionego połączenia sieciowego. Strumienie przeznaczone tylko do zapisu są zwykle nazywane strumieniami wyjściowymi, tak jak w tym przykładzie, i podobnie strumienie przeznaczone tylko do odczytu nazywane są strumieniami wejściowymi, jak w tym przykładzie.

Strumień może przeprowadzać transformację lub kodowanie danych (na przykład SslStream w .Net pochłonie dane negocjacji SSL i ukryje je przed Tobą; TelnetStream może ukryć negocjacje Telnet przed Tobą, ale zapewnia dostęp do danych; A ZipOutputStream w Javie pozwala na zapisywanie w plikach w archiwum zip bez martwienia się o wewnętrzne cechy formatu pliku zip.

Inną powszechną rzeczą, którą możesz znaleźć, są strumienie tekstowe, które umożliwiają pisanie łańcuchów zamiast bajtów, lub niektóre języki zapewniają strumienie binarne, które umożliwiają pisanie typów pierwotnych. Typową rzeczą, którą można znaleźć w strumieniach tekstowych, jest kodowanie znaków, o którym należy wiedzieć.

Niektóre strumienie obsługują również dostęp losowy, jak w tym przykładzie. Z drugiej strony, strumień sieciowy z oczywistych powodów nie.

  • MSDN daje dobry przegląd strumieni w .Net.
  • Sun ma również przegląd ich ogólnej klasy OutputStream i klasy InputStream .
  • W języku C ++ jest to dokumentacja istream (strumień wejściowy), ostream (strumień wyjściowy) i iostream (strumień dwukierunkowy).

Systemy operacyjne podobne do UNIX obsługują również model strumienia z wejściem i wyjściem programu, jak opisano tutaj .

IRBMe
źródło
13

Odpowiedzi udzielone do tej pory są doskonałe. Podaję tylko inny, aby podkreślić, że strumień nie jest sekwencją bajtów ani nie jest specyficzny dla języka programowania, ponieważ koncepcja jest uniwersalna (podczas gdy jej implementacja może być unikalna). Często widzę w Internecie mnóstwo wyjaśnień w zakresie SQL, C lub Java, które mają sens, ponieważ strumień plików zajmuje się lokalizacjami pamięci i operacjami niskiego poziomu. Często jednak omawiają, jak utworzyć strumień plików i operować na potencjalnym pliku w swoim języku, zamiast omawiać koncepcję strumienia.

Metafora

Jak wspomniano, a streamjest metaforą, abstrakcją czegoś bardziej złożonego. Aby Twoja wyobraźnia działała, podaję kilka innych metafor:

  1. chcesz napełnić wodą pusty basen. Jednym ze sposobów na osiągnięcie tego jest podłączenie węża do kranu, umieszczenie końca węża w basenie i włączenie wody.

wąż jest strumieniem

  1. podobnie, gdybyś chciał zatankować gaz do swojego auta, poszedłbyś do dystrybutora, włożył dyszę do baku i otworzył zawór ściskając dźwignię blokującą.

wąż, dysza i powiązane mechanizmy umożliwiające przepływ gazu do zbiornika to strumień

  1. Jeśli potrzebujesz dojechać do pracy, zaczniesz jechać z domu do biura autostradą.

autostrada to strumień

  1. jeśli chcesz z kimś porozmawiać, używałbyś uszu do słuchania i ust do mówienia.

twoje uszy i oczy są strumieniami

Miejmy nadzieję, że zauważysz w tych przykładach, że metafory strumienia istnieją tylko po to, aby pozwolić, aby coś przez nie przepłynęło (lub na niej w przypadku autostrady) i nie zawsze same stawiają to, co przenoszą. Ważna różnica. Nie odnosimy się do naszych uszu jako sekwencji słów. Wąż jest nadal wężem, jeśli nie przepływa przez niego woda, ale musimy go podłączyć do kranu, aby prawidłowo spełniał swoje zadanie. Samochód nie jest jedynym „rodzajem” pojazdu, który może poruszać się po autostradzie.

W ten sposób może istnieć strumień, przez który nie przechodzą żadne dane, o ile jest połączony z plikiem .

Usuwanie abstrakcji

Następnie musimy odpowiedzieć na kilka pytań. Mam zamiar używać plików do opisywania strumieni, więc ... Co to jest plik? A jak czytamy plik? Spróbuję odpowiedzieć na to pytanie, zachowując pewien poziom abstrakcji, aby uniknąć niepotrzebnej złożoności i wykorzystam koncepcję pliku w odniesieniu do systemu operacyjnego Linux ze względu na jego prostotę i dostępność.

Co to jest plik?

Plik to abstrakcja :)

Lub, tak prosto, jak potrafię wyjaśnić, plik składa się z jednej części struktury danych opisującej plik i jednej części danych, które stanowią rzeczywistą zawartość.

Część struktury danych (nazywana i-węzłem w systemach UNIX / Linux) identyfikuje ważne informacje o treści, ale nie zawiera samej treści (ani nazwy pliku w tym zakresie). Jedną z informacji, które przechowuje, jest adres w pamięci, od którego zaczyna się treść. Tak więc mając nazwę pliku (lub twarde łącze w Linuksie), deskryptor pliku (numeryczna nazwa pliku, o którą dba system operacyjny) i początkową lokalizację w pamięci, mamy coś, co możemy nazwać plikiem.

(kluczową kwestią jest to, że „plik” jest definiowany przez system operacyjny, ponieważ ostatecznie to system operacyjny musi sobie z nim radzić. i tak, pliki są znacznie bardziej złożone).

Na razie w porządku. Ale jak otrzymamy zawartość pliku, powiedzieć list miłosny do swojego beau, żebyśmy mogli go wydrukować?

Czytanie pliku

Jeśli zaczniemy od wyniku i cofniemy się, kiedy otworzymy plik na naszym komputerze, cała jego zawartość jest rozpryskiwana na naszym ekranie, abyśmy mogli ją przeczytać. Ale jak? Bardzo metodyczna jest odpowiedź. Zawartość samego pliku to kolejna struktura danych. Załóżmy, że jest to tablica znaków. Możemy również myśleć o tym jako o sznurku.

Jak więc „odczytujemy” ten ciąg? Znajdując jego lokalizację w pamięci i iterując po naszej tablicy znaków, po jednym znaku na raz, aż do osiągnięcia końca znaku pliku. Innymi słowy, program.

Strumień jest „tworzony”, gdy wywoływany jest jego program i ma on miejsce w pamięci, do którego można się podłączyć lub z którym można się połączyć . Podobnie jak nasz przykład z wężem wodnym, wąż jest nieefektywny, jeśli nie jest podłączony do kranu. W przypadku strumienia, aby istniał, musi być połączony z plikiem.

Strumienie można dalej udoskonalać, np. Strumień do odbioru danych wejściowych lub strumień do wysyłania zawartości plików na standardowe wyjście. UNIX / linux łączy i utrzymuje otwarte 3 strumienie plików dla nas od razu, stdin (standardowe wejście), stdout (standardowe wyjście) i stderr (standardowy błąd). Strumienie mogą być budowane jako same struktury danych lub obiekty, co pozwala nam wykonywać bardziej złożone operacje przesyłania danych przez nie, takie jak otwieranie strumienia, zamykanie strumienia lub sprawdzanie błędów pliku, do którego jest podłączony strumień. C ++ cinjest przykładem obiektu typu stream.

Z pewnością, jeśli wybierzesz, możesz napisać własny strumień.

Definicja

Strumień to fragment kodu wielokrotnego użytku, który abstrahuje od złożoności obsługi danych, jednocześnie zapewniając użyteczne operacje do wykonania na danych.

rekurzion
źródło
Tak więc strumień jest medium, przez który przepływają dane. Musi być połączony ze źródłem danych i może wykonywać operacje na sobie i na przepływających przez niego danych.
KingBryan
tak. jedynym powodem napisania dodatkowej odpowiedzi było wyjaśnienie, że strumień może istnieć, nawet jeśli nie przepływają przez niego dane. podczas gdy strumień IRL (z wodą itp.) przestaje być strumieniem, gdy woda przestaje płynąć i może powodować zamieszanie w myśleniu o analogii.
rekurzion
1
Obecnie uczę się języka Java, a Twoja odpowiedź pomogła mi to zrozumieć.
KingBryan
7

Oprócz rzeczy wspomnianych powyżej istnieje inny rodzaj strumieni - zgodnie z definicją w funkcjonalnych językach programowania, takich jak Scheme lub Haskell - prawdopodobnie nieskończona struktura danych, która jest generowana przez jakąś funkcję na żądanie.

EFraim
źródło
6

Inna analogia: nie możesz pływać pod prąd, dlatego możesz po prostu pobrać następny bit, bajt, ciąg lub obiekt ze strumienia, podczas gdy już odczytane dane są usuwane. Bilet w jedną stronę ... lub po prostu kolejka bez przechowywania wytrwałości.

Czy potrzebujemy więc kolejek? Ty decydujesz.

Marcus
źródło
oznacza to, że w strumieniu musisz iść do przodu, nie możesz się cofać. Dodatkowo poprzednie dane są usuwane wraz z postępem, które oszczędzają pamięć?
Prawda
5

Słowo „strumień” zostało wybrane, ponieważ reprezentuje (w prawdziwym życiu) bardzo podobne znaczenie do tego, co chcemy przekazać, kiedy go używamy.

Zacznij myśleć o analogii do strumienia wody. Otrzymujesz ciągły przepływ danych, tak jak woda nieustannie płynie w rzece. Nie zawsze wiesz, skąd pochodzą dane, a najczęściej nie musisz; czy to z pliku, gniazda czy innego źródła, nie ma to (nie powinno) znaczenia. Jest to bardzo podobne do otrzymywania strumienia wody, dzięki czemu nie musisz wiedzieć, skąd pochodzi; czy to z jeziora, fontanny czy innego źródła, to nie ma (nie powinno) mieć znaczenia. źródło

Premraj
źródło