Czy potrafisz wyjaśnić pojęcie strumieni?

186

Rozumiem, że strumień jest reprezentacją sekwencji bajtów. Każdy strumień zapewnia środki do odczytu i zapisu bajtów do danego magazynu kopii zapasowych. Ale jaki jest sens strumienia? Dlaczego sam sklep z podkładami nie wchodzi w interakcje?

Z jakiegokolwiek powodu ta koncepcja po prostu mnie nie kliknie. Przeczytałem kilka artykułów, ale myślę, że potrzebuję analogii lub czegoś takiego.

Rob Sobers
źródło

Odpowiedzi:

234

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

Na chwilę zapomnijmy o sklepie z podkładami i zacznijmy myśleć o analogii do strumienia wody. Otrzymujesz ciągły przepływ danych, tak jak woda stale przepływa w rzece. Niekoniecznie wiesz, skąd pochodzą dane i najczęściej nie musisz; czy to z pliku, gniazda czy innego źródła, to nie powinno (nie powinno) mieć znaczenia. Jest to bardzo podobne do odbierania strumienia wody, dzięki czemu nie musisz wiedzieć, skąd pochodzi; czy to z jeziora, fontanny, czy innego źródła, to nie powinno (nie powinno) mieć znaczenia.

To powiedziawszy, kiedy zaczniesz myśleć, że zależy ci tylko na uzyskaniu potrzebnych danych, bez względu na to, skąd pochodzą, abstrakcje, o których mówili inni, stają się wyraźniejsze. Zaczynasz myśleć, że możesz owijać strumienie, a twoje metody będą nadal działać idealnie. Na przykład możesz to zrobić:

int ReadInt(StreamReader reader) { return Int32.Parse(reader.ReadLine()); }

// in another method:
Stream fileStream = new FileStream("My Data.dat");
Stream zipStream = new ZipDecompressorStream(fileStream);
Stream decryptedStream = new DecryptionStream(zipStream);
StreamReader reader = new StreamReader(decryptedStream);

int x = ReadInt(reader);

Jak widać, bardzo łatwo jest zmienić źródło wejściowe bez zmiany logiki przetwarzania. Na przykład, aby odczytać dane z gniazda sieciowego zamiast pliku:

Stream stream = new NetworkStream(mySocket);
StreamReader reader = new StreamReader(stream);
int x = ReadInt(reader);

Tak proste, jak to tylko możliwe. Piękno trwa, ponieważ można użyć dowolnego źródła wejściowego, o ile można zbudować dla niego „opakowanie” strumienia. Możesz nawet to zrobić:

public class RandomNumbersStreamReader : StreamReader {
    private Random random = new Random();

    public String ReadLine() { return random.Next().ToString(); }
}

// and to call it:
int x = ReadInt(new RandomNumbersStreamReader());

Widzieć? Dopóki twoja metoda nie dba o to, jakie jest źródło wejściowe, możesz dostosowywać swoje źródło na różne sposoby. Abstrakcja pozwala oddzielić dane wejściowe od logiki przetwarzania w bardzo elegancki sposób.

Pamiętaj, że strumień, który sami stworzyliśmy, nie ma sklepu z zapleczem, ale nadal doskonale służy naszym celom.

Podsumowując, strumień jest tylko źródłem danych wejściowych, ukrywającym (abstrakcyjnym) inne źródło. Dopóki nie złamiesz abstrakcji, twój kod będzie bardzo elastyczny.

Hosam Aly
źródło
6
Myślenie abstrakcyjne (i wyjaśnianie) wydaje się być we krwi;) Twoja analogia do wody (a zatem odniesienia metaforyczne) przypominała mi Omara Chajjama.
java.is.for.desktop
@HosamAly Twoje wyjaśnienie jest bardzo jasne, ale coś mnie trochę myli w przykładowym kodzie. Wyraźna konwersja z łańcucha na int odbywa się automatycznie, wykonując ReadInt? wierzę, że mógłbym też zrobić ReadString?
Rushino
1
@ Rushino W powyższym kodzie nie ma konwersji. Metodę ReadIntdefiniuje się na samej górze za pomocą int.Parse, która odbiera zwrócony ciąg reader.ReadLine()i analizuje go. Oczywiście możesz stworzyć podobną ReadStringmetodę. Czy to jest wystarczająco jasne?
Hosam Aly,
Dobrze wyłożone. Strumienie są dla mnie najprostszymi i najmocniejszymi rodzajowymi abstrakcjami w całym programowaniu. Posiadanie .net basic Stream.Copyznacznie ułatwia życie w wielu aplikacjach.
Felype,
38

Chodzi o to, że nie powinieneś wiedzieć, czym jest sklep z podkładami - to abstrakcja. Rzeczywiście, może nie być nawet sklepu z kopiami zapasowymi - możesz czytać z sieci, a dane nigdy nie są „przechowywane”.

Jeśli potrafisz napisać kod, który działa niezależnie od tego, czy rozmawiasz z systemem plików, pamięcią, siecią, czy czymkolwiek innym, co obsługuje pomysł streamowania, twój kod jest znacznie bardziej elastyczny.

Ponadto strumienie są często łączone razem - możesz mieć strumień, który kompresuje wszystko, co się w nim znajduje, zapisując skompresowaną formę w innym strumieniu lub taki, który szyfruje dane itp. Na drugim końcu byłoby odwrotnie łańcuch, deszyfrowanie, dekompresowanie lub cokolwiek innego.

Jon Skeet
źródło
Czy różne typy czytników strumieni używanych w powyższym przykładzie @HosamAly nie sugerują, że wiesz, co to jest sklep kopii zapasowych? Rozumiem, że FileStream, NetworkStream itp. ... czytają z tego rodzaju źródeł. Ponadto, czy są przypadki, w których nie wiesz, jaki może być sklep z podkładem i który byłby dynamicznie wybierany podczas działania programu? Po prostu osobiście tego nie spotkałem i chciałbym dowiedzieć się więcej.
user137717,
Czy strumienie danych mogą być przesyłane strumieniowo przez jakiś proces podczas generowania danych, czy też potrzebuję dostępu do pełnego zestawu danych, na którym chcę operować, gdy rozpoczynam proces?
user137717,
@ user137717: Nie, jeśli weźmiesz StreamReader- lub lepiej - TextReaderto Twój kod nie wie, jaki rodzaj strumienia leży u podstaw przepływu danych. A raczej może użyć BaseStreamwłaściwości do znalezienia typu - ale może to być typ, którego Twój kod nigdy wcześniej nie widział. Chodzi o to, że nie powinieneś się tym przejmować. I tak, możesz absolutnie skończyć pisaniem kodu, który czasami będzie używany dla strumienia sieciowego, a czasem dla strumienia plików. Jeśli chodzi o strumienie przesyłające dane przez proces - cóż, nie byłoby to możliwe w procesie ... to byłby dostawca strumienia.
Jon Skeet
30

Celem strumienia jest zapewnienie warstwy abstrakcji między tobą a sklepem z zapleczem. Dlatego dany blok kodu korzystający ze strumienia nie musi dbać o to, czy magazyn kopii zapasowych jest plikiem dyskowym, pamięcią itp.

Torlack
źródło
Tak, pozwala ci na zamianę rodzaju strumienia bez łamania twojego kodu. Na przykład, możesz odczytać z pliku przy jednym wywołaniu, a następnie z bufora pamięci przy następnym.
Craig
Dodałbym, że powodem, dla którego chcesz to zrobić, jest to, że często nie potrzebujesz możliwości wyszukiwania plików podczas odczytu lub zapisu pliku, a zatem jeśli używasz strumienia, ten sam kod może być łatwo użyty do odczytu lub zapisu do na przykład gniazdo sieciowe.
alxp
11

Nie chodzi o strumienie - chodzi o pływanie. Jeśli potrafisz pływać jednym strumieniem, możesz pływać dowolnym napotkanym strumieniem.

dmajkic
źródło
7

Aby dodać do komory echa, strumień jest abstrakcją, więc nie obchodzi Cię podstawowy sklep. Jest to najbardziej sensowne, gdy rozważasz scenariusze ze strumieniami i bez nich.

Pliki są w większości nieciekawe, ponieważ strumienie nie robią wiele więcej niż metody nie oparte na strumieniach, które znam. Zacznijmy od plików internetowych.

Jeśli chcę pobrać plik z Internetu, muszę otworzyć gniazdo TCP, nawiązać połączenie i odbierać bajty, dopóki nie będzie więcej bajtów. Muszę zarządzać buforem, znać rozmiar oczekiwanego pliku i pisać kod, aby wykryć przerwanie połączenia i odpowiednio go obsłużyć.

Powiedzmy, że mam jakiś obiekt TcpDataStream. Tworzę je z odpowiednimi informacjami o połączeniu, a następnie czytam bajty ze strumienia, aż powie, że nie ma już bajtów. Strumień obsługuje zarządzanie buforem, warunki końca danych i zarządzanie połączeniami.

W ten sposób strumienie ułatwiają operacje wejścia / wyjścia. Z pewnością możesz napisać klasę TcpFileDownloader, która robi to, co robi strumień, ale wtedy masz klasę specyficzną dla TCP. Większość interfejsów strumieniowych po prostu udostępnia metody Read () i Write (), a wszelkie bardziej skomplikowane koncepcje są obsługiwane przez wewnętrzną implementację. Z tego powodu możesz używać tego samego podstawowego kodu do odczytu lub zapisu w pamięci, plikach dyskowych, gniazdach i wielu innych magazynach danych.

OwenP
źródło
5

Wizualizacja, której używam, to pasy transmisyjne, nie w prawdziwych fabrykach, ponieważ nic o tym nie wiem, ale w fabrykach kreskówek, w których przedmioty poruszają się wzdłuż linii i są stemplowane, pakowane w pudełka, liczone i sprawdzane przez sekwencję głupich urządzeń.

Masz proste elementy, które wykonują jedną rzecz, na przykład urządzenie do nakładania wiśni na ciasto. To urządzenie ma strumień wejściowy ciast bez wiśni i strumień wyjściowy ciast z wiśniami. Istnieją trzy zalety, o których warto wspomnieć, tworząc taką strukturę przetwarzania.

Po pierwsze, upraszcza same elementy: jeśli chcesz nałożyć ciasto czekoladowe na lukier, nie potrzebujesz skomplikowanego urządzenia, które wie wszystko o ciastach, możesz stworzyć głupie urządzenie, które przyklei lukier czekoladowy do wszystkiego, co jest do niego podawane (w bajki, chodzi o tyle, że nie wie, że następnym przedmiotem nie jest ciasto, to Wile E. Coyote).

Po drugie, możesz tworzyć różne produkty, umieszczając urządzenia w różnych sekwencjach: może chcesz, aby twoje ciastka miały lukier na wisience zamiast wisienki na wisience, i możesz to zrobić po prostu zamieniając urządzenia na linii .

Po trzecie, urządzenia nie muszą zarządzać zapasami, boksowaniem ani rozpakowywaniem. Najskuteczniejszy sposób agregowania i pakowania rzeczy jest zmienny: może dzisiaj wkładasz swoje ciastka do 48 pudełek i wysyłasz je ciężarówką, ale jutro chcesz wysłać pudełka po sześć w odpowiedzi na niestandardowe zamówienia. Tego rodzaju zmianę można uwzględnić poprzez wymianę lub ponowną konfigurację maszyn na początku i na końcu linii produkcyjnej; maszyna wiśniowa na środku linii nie musi być zmieniana, aby przetwarzać inną liczbę elementów na raz, zawsze działa z jednym elementem na raz i nie musi wiedzieć, w jaki sposób jego wejście lub wyjście jest być zgrupowane.


źródło
Świetny przykład analogii jako wyjaśnienia.
Richie Thomas
5

Kiedy po raz pierwszy usłyszałem o transmisji strumieniowej, było to w kontekście transmisji na żywo za pomocą kamery internetowej. Tak więc jeden host nadaje zawartość wideo, a drugi odbiera treść wideo. Więc to jest streaming? No cóż ... tak ... ale transmisja na żywo jest konkretną koncepcją i myślę, że pytanie odnosi się do abstrakcyjnej koncepcji Streaming. Zobacz https://en.wikipedia.org/wiki/Live_streaming

Przejdźmy więc dalej.


Wideo nie jest jedynym zasobem, który można przesyłać strumieniowo. Dźwięk można również przesyłać strumieniowo. Mówimy teraz o multimediach przesyłanych strumieniowo. Zobacz https://en.wikipedia.org/wiki/Streaming_media . Dźwięk może być dostarczany od źródła do celu na wiele sposobów. Porównajmy zatem niektóre metody dostarczania danych.

Klasyczne pobieranie plików Klasyczne pobieranie plików nie odbywa się w czasie rzeczywistym. Przed użyciem pliku musisz poczekać na zakończenie pobierania.

Pobieranie progresywne Fragmenty pobierania progresywnego pobierają dane ze strumieniowego pliku multimedialnego do bufora tymczasowego. Dane w tym buforze są wykonalne: dane audio-wideo w buforze można odtwarzać. Z tego powodu użytkownicy mogą oglądać / słuchać przesyłanego strumieniowo pliku multimedialnego podczas pobierania. Możliwe jest szybkie przewijanie do przodu i do tyłu, zjazd z bufora. W każdym razie progresywne pobieranie nie jest przesyłaniem strumieniowym na żywo.

Streaming Dzieje się w czasie rzeczywistym i dzieli dane. Streaming jest realizowany w transmisjach na żywo. Klienci słuchający transmisji nie mogą przewijać do przodu ani do tyłu. W strumieniach wideo dane są odrzucane po odtworzeniu.

Serwer przesyłania strumieniowego utrzymuje dwukierunkowe połączenie ze swoim klientem, a serwer sieciowy zamyka połączenie po odpowiedzi serwera.


Audio i wideo to nie jedyne rzeczy, które można przesyłać strumieniowo. Rzućmy okiem na pojęcie strumieni w podręczniku PHP.

strumień jest obiektem zasobu, który wykazuje zachowanie do strumieniowania. Oznacza to, że może być odczytywany lub zapisywany w sposób liniowy i może być w stanie fseek () w dowolnym miejscu w strumieniu. Link: https://www.php.net/manual/en/intro.stream.php

W PHP zasób to odwołanie do zewnętrznego źródła, takiego jak plik, połączenie z bazą danych. Innymi słowy, strumień jest źródłem, z którego można odczytać lub zapisać. Więc jeśli pracowałeś fopen(), to już pracowałeś ze strumieniami.

Przykład pliku tekstowego, który podlega strumieniowaniu:

// Let's say that cheese.txt is a file that contains this content: 
// I like cheese, a lot! My favorite cheese brand is Leerdammer.
$fp = fopen('cheese.txt', 'r');

$str8 = fread($fp, 8); // read first 8 characters from stream. 

fseek($fp, 21); // set position indicator from stream at the 21th position (0 = first position)
$str30 = fread($fp, 30); // read 30 characters from stream

echo $str8; // Output: I like c 
echo $str30; // Output: My favorite cheese brand is L

Pliki zip można również przesyłać strumieniowo. Ponadto przesyłanie strumieniowe nie ogranicza się do plików. Można również przesyłać strumieniowo połączenia HTTP, FTP, SSH oraz wejścia / wyjścia.


Co wikipedia mówi o koncepcji Streaming?

W informatyce strumień jest sekwencją elementów danych udostępnianych w miarę upływu czasu. Strumień można traktować jako przedmioty przetwarzane pojedynczo na taśmie przenośnika, a nie w dużych partiach.

Zobacz: https://en.wikipedia.org/wiki/Stream_%28computing%29 .

Wikipedia prowadzi do tego: https://srfi.schemers.org/srfi-41/srfi-41.html, a autorzy mają to do powiedzenia na temat strumieni:

Strumienie, czasem nazywane leniwymi listami, to sekwencyjna struktura danych zawierająca elementy obliczane tylko na żądanie. Strumień ma wartość null lub jest parą ze strumieniem w cdr. Ponieważ elementy strumienia są obliczane tylko po uzyskaniu dostępu, strumienie mogą być nieskończone.

Strumień jest więc strukturą danych.


Mój wniosek: strumień jest źródłem, które może zawierać dane, które można odczytywać lub zapisywać sekwencyjnie. Strumień nie odczytuje wszystkiego, co zawiera źródło, odczytuje / zapisuje sekwencyjnie.


Przydatne linki:

  1. http://www.slideshare.net/auroraeosrose/writing-and-using-php-streams-and-sockets-zendcon-2011 Zapewnia bardzo przejrzystą prezentację
  2. https://www.sk89q.com/2010/04/introduction-to-php-streams/
  3. http://www.netlingo.com/word/stream-or-streaming.php
  4. http://www.brainbell.com/tutorials/php/Using_PHP_Streams.htm
  5. http://www.sitepoint.com/php-streaming-output-buffering-explained/
  6. http://php.net/manual/en/wrappers.php
  7. http://www.digidata-lb.com/streaming/Streaming_Proposal.pdf
  8. http://www.webopedia.com/TERM/S/streaming.html
  9. https://en.wikipedia.org/wiki/Stream_%28computing%29
  10. https://srfi.schemers.org/srfi-41/srfi-41.html
juliański
źródło
4

To tylko koncepcja, kolejny poziom abstrakcji, który ułatwia życie. Wszystkie mają wspólny interfejs, co oznacza, że ​​można je łączyć w potoku. Na przykład, koduj do base64, następnie zip, a następnie zapisz to na dysk i wszystko w jednym wierszu!

vava
źródło
Z pewnością jest to przydatne, ale nie powiedziałbym, że to „cała sprawa”. Nawet bez tworzenia łańcuchów warto mieć wspólną abstrakcję.
Jon Skeet
Tak, masz rację. Zmieniłem słowa, aby to wyjaśnić.
vava
Tak, tak jest lepiej. Mam nadzieję, że nie pomyślałeś, że byłem zbyt wybredny!
Jon Skeet
3

Najlepszym wyjaśnieniem strumieni, jakie widziałem, jest rozdział 3 SICP . (Może być konieczne przeczytanie pierwszych 2 rozdziałów, aby miało to sens, ale i tak powinieneś. :-)

W ogóle nie używają steramów jako bajtów, ale raczej liczby całkowite. Najważniejsze punkty, które z tego wyciągnąłem:

  • Strumienie są listami opóźnionymi
  • Obciążenie obliczeniowe [w niektórych przypadkach chętnie oblicza wszystko z wyprzedzeniem] jest oburzające
  • Możemy używać strumieni do reprezentowania sekwencji, które są nieskończenie długie
Rozpoznać
źródło
Aktualnie jestem w rozdziale 1 SICP. Dzięki!
Rob Sobers
2
jeden chciałby powiedzieć strumień SICP od innych. Ważną cechą strumienia SICP jest lenistwo , a generic stream pojęcie podkreśla abstrakcję na sekwencji danych .
象 嘉 道
2

Kolejna kwestia (do odczytu sytuacji pliku):

  1. streammoże pozwolić ci zrobić coś innego wcześniej finished reading all content of the file.
  2. możesz zaoszczędzić pamięć, ponieważ nie musisz ładować całej zawartości pliku na raz.
Vikyd
źródło
1

Pomyśl o strumieniach jako o abstrakcyjnym źródle danych (bajty, znaki itp.). Abstrahują od rzeczywistej mechaniki odczytu i zapisu do konkretnego źródła danych, czy to gniazda sieciowego, pliku na dysku czy odpowiedzi z serwera WWW.

Anton Gogolev
źródło
1

Myślę, że należy wziąć pod uwagę, że sam sklep z podkładami jest często tylko kolejną abstrakcją. Strumień pamięci jest dość łatwy do zrozumienia, ale plik różni się diametralnie w zależności od używanego systemu plików, nieważne jakiego dysku twardego używasz. Nie wszystkie strumienie faktycznie znajdują się na szczycie sklepu z zapleczem: strumienie sieciowe są po prostu strumieniami.

Rzecz w tym, że ograniczamy naszą uwagę do tego, co ważne. Dzięki standardowej abstrakcji możemy wykonywać typowe operacje. Nawet jeśli nie chcesz na przykład dzisiaj wyszukiwać pliku lub odpowiedzi HTTP na adresy URL, nie oznacza to, że nie będziesz chciał jutro.

Strumienie pierwotnie powstało, gdy pamięć była niewielka w porównaniu do pamięci. Sam odczyt pliku C może być znaczącym obciążeniem. Minimalizacja zajmowanego miejsca była niezwykle ważna. Dlatego bardzo przydatna była abstrakcja, w której bardzo niewiele trzeba było załadować. Dzisiaj jest równie przydatny podczas komunikacji sieciowej i, jak się okazuje, rzadko jest tak restrykcyjny, gdy mamy do czynienia z plikami. Możliwość przezroczystego dodawania ogólnych elementów, takich jak buforowanie, czyni je jeszcze bardziej użytecznymi.

Julian Birch
źródło
0

Strumień to abstrakcja ciągu bajtów. Chodzi o to, że nie musisz wiedzieć, skąd pochodzą bajty, tylko że możesz je odczytać w znormalizowany sposób.

Na przykład, jeśli przetwarzasz dane za pośrednictwem strumienia, nie ma znaczenia dla twojego kodu, czy dane pochodzą z pliku, połączenia sieciowego, ciągu, obiektu blob w bazie danych itp. Itp.

Nie ma w sobie nic złego w interakcji z samym sklepem z podkładem, z wyjątkiem tego, że wiąże cię on z implementacją sklepu z podkładem.

Sean
źródło
0

Strumień to abstrakcja zapewniająca standardowy zestaw metod i właściwości do interakcji z danymi. Abstrahując od rzeczywistego nośnika pamięci, kod można napisać bez całkowitego polegania na tym nośniku, a nawet implementacji tego nośnika.

Dobrą analogią może być rozważenie torby. Nie obchodzi cię, z czego zrobiona jest torba ani co robi, kiedy wkładasz do niej swoje rzeczy, pod warunkiem, że torba spełnia rolę torby i możesz odzyskać swoje rzeczy. Strumień definiuje dla nośników danych to, co definiuje pojęcie torby dla różnych jej przypadków (takich jak worek na śmieci, torebka, plecak itp.) - zasady interakcji.

Jeff Yates
źródło
0

Krótko mówiąc, brakowało mi tutaj słowa:

Strumienie to kolejki zwykle przechowywane w buforze zawierające wszelkiego rodzaju dane.

(Teraz, ponieważ wszyscy wiemy, jakie są kolejki, nie trzeba tego dalej wyjaśniać).

Marcus
źródło