Strumienie oraz operacje na plikach Uwaga: Tekst zamieszczony w tej sekcji był przygotowany jako wstęp do tematu implementacji strumieni w języku C/C++. Samo pojęcie strumieni jest na tyle uniwersalne, że tekst może posłużyć równie dobrze jako wstęp do tematu implementacji strumienie w VB.Net. Strumienie Pojęcie strumienia najłatwiej chyba wytłumaczyć na przykładzie zbiornika z wodą, mającego zawór wlotowy i zawór wylotowy. Aby napełnić zbiornik (wprowadzić dane do strumienia) należy odkręcić zawór wlotowy. Wtedy woda zacznie do zbiornika napływać (bufor strumienia zacznie zapełniać się danymi jeśli oczywiście taki bufor istnieje). Gdy otworzony zostanie zawór wylotowy, woda zacznie ze zbiornika wypływać (odpowiada to pobieraniu danych ze strumienia). Jeśli woda szybciej wypływa niż napływa (dane ze strumienia szybciej są pobierane niż dostarczane), zbiornik opróżnia się całkowicie (strumień danych staje się pusty). Jeśli zaś woda napływa szybciej niż odpływa (bufor strumienia zapełniany jest szybciej niż opróżniany), następuje przepełnienie. Jedyną istotną różnicą pomiędzy strumieniem danych a jego wodną analogią jest fakt, iż cząsteczki wody mogą mieszać się ze sobą, zaś kolejność danych w strumieniu nie może ulec zmianie. Co więcej, dla strumieni możliwe są do wykonania operacje pobierania, zwracania, jak również przewijania danych (choć nie dotyczy to wszystkich zaimplementowanych strumieni). W odniesieniu do zbiornika z wodą miałoby to odpowiednik w przetłaczania wody w różnych kierunkach poprzez jego zawory. Najważniejszym celem, któremu przyświecało zaimplementowanie strumieni było opracowanie metody pozwalającej na łatwe korzystanie z ekranu monitora, klawiatury oraz zasobów dyskowych. To właśnie strumienie pozwalają programiście zapomnieć o szczegółach realizacji operacji we/wy. Wystarczy, aby strumień został skojarzony z jakimś urządzeniem, aby przesyłanie danych ograniczyło się do wykorzystania własności samego strumienia. Przy pracy ze strumieniami wyróżnić można trzy zasadnicze kroki. Pierwszym jest nawiązanie połączenia z urządzeniem i skojarzenie go ze strumieniem (mówi się o otwarciu strumienia). Drugim jest przesyłanie danych. Trzecim krokiem jest zamknięcie połączenia (mówi się o zamykaniu strumienia). Zarówno otwarcie strumienia, jak i przesyłanie danych oraz jego zamknięcie realizuje się poprzez funkcje operujące na strumieniu reprezentującym połączenie. Każdy otwarty w programie strumień powinien zostać zamknięty zanim program zakończy się. Jeśli program zakończy się, a któryś ze strumieni pozostanie otwarty, jego zamknięciem zajmie się system operacyjny. Jednak zamknięcia strumienia przez system operacyjny jest sytuacja niepożądaną. Może ona prowadzić do poważnych błędów i utraty danych. Kiedy program zaczyna swoje działanie, kod inicjujący automatycznie otwiera kilka strumieni: standardowe wejście (stdin), standardowe wyjście (stdout), oraz standardowe wyjście błędów (stderr). Zgodnie z nazwą wejście normalnie służy jako źródło danych do programu, wyjście pozwala wyprowadzać dane z programu, strumień błędu zaś pozwala wyprowadzać komunikaty o błędach. Dzięki strumieniom praktycznie nie ma żadnej różnicy pomiędzy odczytywaniem danych z pliku a odczytywaniem danych z klawiatury. Podobnie, jeśli wyjście programu składa się ze znaków
alfanumerycznych i interpunkcyjnych, nie ma żadnej różnicy między zapisem danych do pliku, a wypisaniem ich na ekran komputera lub wyprowadzeniem do potoku (pipe) następnego programu. Strumień wejściowy domyślnie skojarzony jest z klawiaturą, strumień wyjściowy i strumień błędu - z ekranem komputera. Jednak skojarzenie to można zmienić. Istnieją na to dwa sposoby. Pierwszy sposób polega na wykorzystaniu możliwości samego systemu operacyjnego. W tym przypadku o skojarzeniu strumieni decyduje format komendy uruchamiającej program (wydanej w linii poleceń). Drugi sposób polega na wykorzystaniu przez programistę możliwości bibliotek we/wy. W tym przypadku skojarzenie strumieni zaimplementowane jest w kodzie źródłowym programu. Składnia komend wydawanych w linii poleceń może być różna w różnych systemach operacyjnych. Zazwyczaj standardowe wejście kojarzy się z plikiem wydając komendę: mprog < plikwe. Komenda ta znaczy tyle, co: uruchom program mprog kojarząc ze standardowym wejściem plik o nazwie plikwe (a nie klawiaturę!). Podobnie przekierować można strumień wyjściowy: mprog > plikwy. Użycie operatora > w podanym przykładzie spowoduje, że dane wyjściowe z programu mprog zapisane zostaną w pliku o nazwie plikwy (jeśli plik o podanej nazwie nie istnieje, to zostanie on stworzony, w przeciwnym razie system operacyjny może zgłosić błąd). Z poziomu komend można również skojarzyć wyjście jednego programu z wejściem do programu drugiego. Odpowiednia komenda miałaby postać: mprog1 mprog2, gdzie operator występujący w tej komendzie tworzy potok. Przykłady najczęściej używanych komend służących przekierowaniu strumieni w systemie operacyjnym UNIX podane są w poniższej tabeli. Znaczenie: uruchom program prog i... komenda (csh) komenda (sh) skojarz stdout z plikiem file prog > file prog > file skojarz stderr z plikiem file prog 2> file skojarz stdout i stderr z plikiem file prog >& file prog > file 2>&1 skojarz stdin z plikiem file prog < file prog < file skojarz stdout z plikiem file (dopisuj do końca) prog >> file prog >> file skojarz stderr z plikiem file (dopisuj do końca) prog 2>> file skojarz stdout i stderr z plikiem file (dopisuj do końca) prog >>& file prog >> file 2>&1 skojarz stdin z klawiaturą i czytaj dopóki c prog <<c prog <<c utwórz potok (stdout programu prog do prog2) prog prog2 prog prog2 utwórz potok (stdout i stderr programu prog do prog2) prog & prog2 prog 2>&1 prog2 Uwaga: Różne systemy operacyjne (a nawet różne powłoki tego samego systemu) mogą różnić się pod względem składni komend i ich możliwości. Większość z nich pozwala jednak na jednoczesne przekierunkowanie standardowego wejście i wyjścia w jednym poleceniu: prog < plikwe > plikwy (czytanie z pliku plikwe i pisanie do pliku plikwy). Niektóre z nich pozwalają zapisać stderr w trzecim pliku. W unix owej powłoce bash odpowiednia komenda miałaby postać: prog < plikwe > plikwy 2> plikerr Rozdzielenie stdout od stderr może być nieco bardziej skomplikowane dla innych powłok (w przypadku csh osiąga się to przez wydanie polecenia:
(prog > plikwy) >& plikerr ) Co więcej, w niektórych systemach operacyjnych istnieją specjalne pliki systemowe na stałe związane ze standardowymi strumieniami (jak np. unix owe /dev/stdin, /dev/stdout, /dev/stderr). Korzystanie z takich plików nie różni się od korzystania ze zwykłych plików. Pliki Tryby dostępu do plików i typy plików Zazwyczaj dane w systemach komputerowych przechowywane są w postaci plików zapisanych na dyskach. Dyski twarde typowych komputerów mają dużo większe pojemności niż dostępna pamięć operacyjna. Stąd też mogą one przechowywać znacznie większe ilości danych niż pomieściłaby pamięć operacyjna. Co więcej, dane zapisane na dyskach nie znikają w chwili wyłączenia zasilania. Można je przenosić pomiędzy komputerami, można je współdzielić pomiędzy różnymi systemami, można je wymieniać pomiędzy programami. Podczas pracy z plikami nie trzeba zbytnio obciążać pamięci operacyjnej. Wystarczy do niej wczytać część danych z pliku, przetworzyć je, i, jeśli to konieczne, wczytać kolejną ich porcję. Aby można było pliki tworzyć i z nich korzystać w VB zaimplementowano szereg narzędzi funkcji umożliwiających odczytywanie, modyfikację i zapisywanie danych na dysku oraz zadeklarowano szereg zmiennych globalnych parametrów określających sposób wykonywanych operacji. W ogólności programy mogą sięgać do zasobów dyskowych na dwa sposoby: sekwencyjny i bezpośredni. Funkcje umożliwiające te dwa sposoby dostępu pozwalają zaimplementować praktycznie każdą operację zapisu lub odczytu. Jednak zanim rozpocznie się jakiekolwiek przetwarzanie danych, sam plik powinien zostać otworzony, a po zakończeniu przetwarzania zamknięty (podobnie jak to jest z robieniem notatek w notesie. Jeśli chce się coś w notesie zapisać, najpierw należy go otworzyć, a po dokonaniu zapisu, notes należy zamknąć). Ponadto dane w plikach mogą mieć dwojaki charakter. Można je traktować jako zestaw znaków (pliki tekstowe) lub też jako zestaw bajtów (pliki binarne). O tym, w jakim trybie dostępu dane z pliku będą odczytywane, zapisywane bądź usuwane, decyduje sam programista. W części z przypadków dostęp do plików może być zrealizowany dwojako: zarówno sekwencyjnie i jak i bezpośrednio. W takich razach do programisty należeć będzie obowiązek zapewnienia poprawności wykonywania operacji zapisu i odczytu danych. Uwaga: Ilość na raz otwartych plików nie może być nieskończona. Nie pozwalają na to systemy operacyjne (gdzie się określa maksymalną liczbę otwartych plików - w systemie DOS liczba ta ukryta jest w zmiennej środowiskowej FILES, zadeklarowanej w pliku CONFIG.SYS) oraz same biblioteki wejścia/wyjścia (np. w Borland C++ maksymalną liczbę otwartych plików określa stała HANDLE_MAX, przy czym maksymalna liczba otwartych plików jest większa dla deskryptorów niż dla strumieni). Pliki o dostępie sekwencyjnym Dane muszą być odczytywane w tej samej kolejności, w jakiej zostały zapisane. Jest to tak jak przy zapisie i odczycie znaków wykonywanym na przesuwającej się taśmie w jakiej kolejności znaki zostały na taśmie zapisane, w takiej kolejności można je odczytać. Podobnie też jak w przypadku taśmy, w środek pliku sekwencyjnego nie można wstawiać nowych danych - taśma jest już zapisana i wstawianie danych w jej środek spowodowałoby zapisanie (utratę) starych danych. Aby wstawić coś w środek taśmy należy właściwie stworzyć nową taśmę, przepisując na nią dane wszystkie dane, wstawiając po drodze w zadanym miejscu nowe dane.
Pliki o dostępie bezpośrednim W ich przypadku dane mogą być odczytywane lub zapisywane w dowolnej kolejności i miejscu. Pewną analogią to tego typu dostępu do danych są płyty CD z muzyką. Jeśli interesuje nas jakiś utwór na płycie, wybieramy go, a sam odtwarzacz przeskakuje do wybranej pozycji, pomijając wszelkie inne utwory. Ten typ dostępu do pliku umożliwia szybkie przeszukiwanie, dodawanie, odczytywanie, zmianę i kasowanie informacji w pliku. Rekordy w pliku o dostępie bezpośrednim Przetwarzanie plików w sposób sekwencyjny należy do wolnych operacji, chyba, że dane odczytywane są z pliku do pamięci operacyjnej komputera i tam dopiero przetwarzane. Odczyt danych może odbywać się kawałkami, aby na raz nie obciążać zbyt mocno pamięci. W ogólności, dane w pliku mogą mieć postać rekordów. Rekord wpisywany do lub odczytywany z pliku ma postać analogiczną do struktur w języku programowania. Jest on kolekcją jednej lub większej ilości pewnych wartości, odpowiadających polom struktur używanym w programie (kiedy jakiś rekord jest odczytywany z dysku, to jest on wpisywany do struktury danych, którą następnie przetwarza program). W większości przypadków plików o dostępie sekwencyjnym rekordy mają różną długość. Dzieje się tak, gdyż przy zapisie lub odczycie wartości jakiejś danej (znak, słowo, ciąg znaków, liczba) rozmiar rekordu odpowiada rozmiarowi danej. A co za tym idzie, wskaźnik położenia może przesuwać się w pliku ze zmiennym skokiem, odpowiednio do rozmiary danych. Idea bezpośredniego dostępu do danych w pliku jest bardzo prosta. Można ją wyjaśnić na przykładzie płyty CD z muzyką. Jeśli interesuje nas jakiś utwór na płycie, to nie musimy przesłuchiwać wszystkich innych wcześniejszych utworów, jak ma to miejsce w odtwarzaniu sekwencyjnym. Wystarczy, że przeskoczymy do wybranego utworu, a zaraz będzie on gotowy do odtworzenia. Właśnie te skoki sprawiają, że bezpośredni dostęp do pliku jest szybszy niż dostęp sekwencyjny. Funkcje umożliwiające skoki traktują plik jak wielką tablicę, w której kolejność dodawania, wypisywania oraz usuwanie elementów jest dowolna. Większość plików o bezpośrednim dostępie posiada rekordy o znanej długości. Najłatwiej jest obsługiwać pliki, w których każdy rekord (zazwyczaj wiersz w pliku) ma dokładnie taki sam rozmiar. Dzięki temu z góry można przewidzieć, jaką pozycję w pliku będzie zajmował wybrany rekord. Zaleta ta jest jednak okupiona nieoptymalnym wykorzystaniem zasobów dyskowych. Jeśli bowiem w pliku umieszczone zostaną jakieś dane, które nie w pełni wypełniają obszar rekordu, na dysku pojawią się rekordy z niewykorzystanymi polami. Oczywiście pliki o dostępie bezpośrednim mogą być odczytywane w sposób sekwencyjny. Natomiast pliki o dostępie sekwencyjnym wcale nie muszą dać się odczytać w sposób bezpośredni.
Pliki w Visual Basic Wykonywanie operacji na plikach w VB 6 na początek może wydać się nieco kłopotliwe. Główny problem tkwi w tym, iż dostęp do plików może być realizowany poprzez różne biblioteki funkcji i klasy (VB for applications dokłada też tu swoje trzy grosze). Istnieją takie funkcje jak Open, Input, Output, Append. Dostępny jest również File System Object (FSO) dostarczający obiektowego podejścia do implementacji operacji na plikach. Po krótkiej chwili można zorientować się, czym są faktycznie dostarczone narzędzia i co można nimi wykonać. VB.Net rozszerza możliwości wykonywania operacji na plikach, zapewniając przy tym kompatybilność z poprzednimi wersjami funkcji wejścia/wyjścia. W VB.Net obsługa plików realizowana jest poprzez przestrzeń nazw System.IO, która duplikuje i rozszerza znany z poprzednie wersji języka File System Object. Dodatkowo przestrzeń nazw System.IO.File zawiera funkcje zapewniające kompatybilność ze starszymi funkcjami wejścia/wyjścia. Reasumując - VB umożliwia przetwarzanie napędów, folderów, plików na szereg sposobów: za pomocą modelu.net System.IO, za pomocą metod tradycyjnych (jak FileOpen, Write), oraz przez model File System Object (FSO). Model.Net System.IO Model System.IO jest modelem obiektowym. W przestrzeni nazw System.IO zawarte są klasy, które pozwalają czytać dane z i pisać dane do plików fizycznych bądź też strumieni. Są tam też klasy pozwalające wykonywać operacje na systemie plików (jak np. wylistowywanie katalogów, monitorowanie zmian w systemie plików, itp.). W VB.Net wprowadzono możliwość asynchronicznego czytania i pisania plików. Podobnie jak w modelu FSO przetwarzanie tekstów i danych binarnych odbywa się przez wywołania o standardowej składni: obiekt.metoda dla konkretnych właściwości, metod i zdarzeń. W przestrzeni nazw System.IO umieszczono, które operują na ciągach znaków, znakach, oraz pozwalają operować na plikach (tekstowych i binarnych). Klasy te zawierają właściwości, metody oraz zdarzenia do: tworzenia, kopiowania, przesuwania i usuwania plików. Najczęściej używane klasy to: FileStream BinaryReader, BinaryWriter, StreamReader, StreamWriter. File FileInfo klasa File dostarcza metod statycznych do tworzenia, kopiowania, usuwania, przesuwania, otwierania plików oraz wspomaga tworzenie obiektów typu FileStream. klasa FileInfo: dostarcza metod instancyjnych do tworzenia, kopiowania, usuwania, przesuwania, otwierania plików oraz wspomaga tworzenie obiektów typu FileStream.
Klasa FileStream tworzy strumień (obiekt klasy Stream) na pliku, dostarczając metod do wykonywania synchronicznego lub asynchronicznego operacji czytania i pisania. Istnieje klasa pomocnicza Path, pozwalająca manipulować ciągami znaków reprezentującymi foldery lub ścieżki dostępu do plików. Tytułem komentarza: klasa FileStream jest opakowaniem nałożonym na plik. W ogólności strumienie można opakowywać innymi strumieniami, dostarczając w ten sposób specjalizowanych funkcji do czytania i pisania danych. Dzięki takim opakowywaniom można: buforować dane w strumieniach, przewijać strumienie, dokonywać bardziej zaawansowanych operacji czytania i pisania. W poniższej tabeli przedstawione są enumeracje związane z obsługą plików VB.Net: Enumeracja Przeznaczenie FileAcces definiuje stałe dla dostępu do pliku w trybie czytania, pisanie, czytania/pisania FileMode określa jak plik ma być otworzony przez system operacyjny FileShare dostarcza stałych do sterowania poziomem dostępu innym członkom FileStream do tego samego pliku FileAttributes określa dostęp do atrybutów zapisanych, dzięki którym można rozpoznać, czy dany plik jest napędem, czy jest zakodowany, ukryty lub tylko do odczytu, lub czy jest to plik systemowy czy też tymczasowy NotifyFilters specyfikuje zmiany, które będą obserwowane w pliku lub folderze SeekOrigin dostarcza pól reprezentujących punkty odniesienia w strumieniu używane podczas przewijania WatcherChandeTypes Zmiany, które mogą wystapić dla pliku lub katalogu Enumeracje FileAccess, FileMode oraz FileShare zawierają flagi używane przez konstruktory klasy FileStream. Enumeracja FileEnumerator dostarcza sposobu na odczytywanie informacji zarządzanych przez system plików hosta dla określonej grupy plików i katalogów. Przykładem takiej informacji może być czas utworzenia i dostępu. Operacje na katalogach We wcześniejszych wersjach VB do wylistowania katalogów służyła funkcja Dir(). Sposób jej wykorzystania przedstawia poniższy przykład: Dim sfile As String 'Obtain an initial value for sfile sfile = Dir("C:") 'Loop until Dir() returns empty string Do Until sfile = vbnullstring Debug.Print sfile sfile = Dir() Loop Realizacja tego samego zadania w VB 6.0 za pomocą Microsoft Scripting Runtime Library oraz obiektu Scripting.FileSystemObject wygląda następująco: Dim ofs As Scripting.FileSystemObject Dim ofolder As Scripting.Folder
Dim ofile As Scripting.File 'Create the FileSystemObject Object Set ofs = New Scripting.FileSystemObject 'Get reference to folder through FileSystemObject Set ofolder = ofs.getfolder("c:\") 'Enumerate Files For Each ofile In ofolder.files Debug.Print ofile.name Next ofile Z kolei w VB.NET zadanie listowania katalogów można zrealizować za pomocą klasy System.IO.Directory. Oto przykład: Try ' Only get files that begin with the letter "c." Dim dirs As String() = Directory.GetFiles("c:\", "c*") Console.WriteLine("The number of files starting with c is {0}.", dirs.length) Dim dir As String For Each dir In dirs Console.WriteLine(dir) Next Catch ee As Exception Console.WriteLine("The process failed: {0}", ee.tostring()) End Try Pliki binarne Pliki binarne tym różnią się od plików tekstowych, że mogą zawierać bajty danych, które nie są tekstem. Operacje czytania lub pisania ze strumienia można realizować metodami Read i Write klasy FileStream. Metody te pobierają ze strumienia lub umieszczają w strumieniu kolejne bajty. Stąd, aby przeczytać z pliku kolejne bajty, lub też do pliku kolejne bajty zapisać, można posłużyć się klasą FileStream. Poniższy przykład obrazuje, jak zapisać dane do pliku binarnego i jak je odczytać. Najpierw piszemy do pliku bajty, później z niego bajty czytamy. Wykorzystanie statycznych metod File ' nazwy plikow do pisania i czytania Dim OutFile As String = "c:\somefile.bin" Dim InFile As String = "c:\somefile.bin" Dim x As Integer Dim Foo(100) As Byte 'data to write Dim afoo(100) As Byte 'data to read Dim aretval As Integer Wykorzystanie instancyjnych metod FileInfo ' nazwy plikow do pisania i czytania Dim OutFile As String = "c:\somefile.bin" Dim InFile As String = "c:\somefile.bin" Dim x As Integer Dim Foo(100) As Byte 'data to write Dim afoo(100) As Byte 'data to read Dim aretval As Integer ' obiekty FileInfo Dim inf As New System.IO.FileInfo(InFile) Dim outf As New System.IO.FileInfo(OutFile) ' strumienie do pisania i czytania Dim myoutstream As System.IO.FileStream Dim myinstream As System.IO.FileStream ' strumienie do czytania i pisania Dim myinstream As System.IO.FileStream Dim myoutstream As System.IO.FileStream
For x = 0 To 100 afoo(x) = x Next Try myoutstream = File.OpenWrite(OutFile) myoutstream.write(afoo, 0, 11) myoutstream.flush() myoutstream.close() myinstream = File.OpenRead(InFile) aretval = myinstream.read(foo, 0, 11) 'read 10 bytes myinstream.close() ' Close the files Catch IOExcep As IO.IOException ' Some kind of error occurred. Display error message MessageBox.Show(IOExcep.Message) End Try For x = 0 To 100 Foo(x) = x Next Try myoutstream = outf.openwrite myoutstream.write(foo, 0, 10) myoutstream.flush() myoutstream.close() myinstream = inf.openread aretval = myinstream.read(afoo, 0, 10) 'read 10 bytes myinstream.close() Catch IOExcep As IO.IOException ' Some kind of error occurred. Display error message MessageBox.Show(IOExcep.Message) End Try Jeśli operacje czytania z lub pisania do pliku mają być wykonane dla danych o objętości większej niż 1 bajt, to wtedy operowanie na surowych bajtach staje się niewygodne. Dlatego do wykonywania bardziej złożonych operacji na blikach binarnych w VB.Net dostarczono klasy BinaryReader oraz Binary Writer (w poprzedniej wersji języka odpowiednikami tych klasy były DataReader oraz DataWriter). BinaryReader służy do czytania ciągów znaków lub podstawowych typów danych ze strumienia, zaś BinaryWriter służy do zapisu ciągów znaków lub podstawowych typów do strumienia. Operacje na podstawowych typach danych zaimplementowane są jako metody o nazwie odpowiadającej typowi. Na przykład metoda ReadDouble służy do odczytywania ze strumienia danych od razu 8 bajtów, które zinterpretowane zostaną jako bajty liczby typu Double. Pliki tekstowe Pliki tekstowe przechowują tekst a więc znaki, które są w ogólności czytelnymi znakami ASCII. Szczególnymi znakami w plikach tekstowych są znaki końca linii (oprócz oczywiście znaku końca pliku). Poniższy przykład obrazuje, jak odczytać i zapisać kawałek tekstu z i do pliku. Dim OutFile As String = "utest.txt" Dim InFile As String = "test.txt" Dim LinesCounted As Long ' create streams for input and output Dim myinstream As System.IO.StreamReader Dim myoutstream As System.IO.StreamWriter ' temporary string to hold work Dim mystr As String = " " ' initialize to not empty Dim OutStr As String = " " Try myinstream = File.OpenText(InFile) ' Open a new stream for input. ' Do until the stream returns Nothing at end of file. myoutstream = File.CreateText(OutFile) Do Until IsNothing(mystr) mystr = myinstream.readline ' perform conversion OutStr = UCase(mystr) myoutstream.writeline(outstr)
LinesCounted += 1 ' Increment line count Loop Catch eof As IO.EndOfStreamException ' No action is necessary, the end of the stream has been reached. Catch IOExcep As IO.IOException ' Some kind of error occurred. MessageBox.Show(IOExcep.Message) Finally myinstream.close() ' Close the files myoutstream.close() End Try Dopisywanie do plików Dopisywanie danych do plików polega na ich otwarciu i umieszczaniu nowych danych na jego końcu. Z punktu widzenia programisty kod realizujący dopisywanie do pliku niewiele różni się od kodu pisania do pliku. Jedyna różnica polega na sposobie otwarcia strumienia: pisanie do pliku myoutstream = File.CreateText dopisywanie do pliku myoutstream = File.AppendText Strumienie standardowe Poniższy przykład pokazuje, jak można skojarzyć standardowy strumień wejścia z obiektem klasy StreamReader. Jeśli moduł zostanie skompilowany do wykonywalnego programu ConsoleApplication3.exe to dla pliku ttt.txt zawierającego cztery linie: 1 2 3 4 wywołanie: ConsoleApplication3.exe < ttt.txt spowoduje wyświetlenie na ekranie sekwencji: 2 4 6 8 Imports System.Text Imports System.Console Imports System.IO Module Module1 Sub Main() Mnozenie() End Sub
Sub Listing() Dim stdin As StreamReader, stdout As System.IO.StreamWriter Dim s As String stdin = New StreamReader(System.Console.OpenStandardInput()) stdout = New StreamWriter(System.Console.OpenStandardOutput()) Do s = stdin.readline() If Not s Is Nothing Then Console.WriteLine(s) Else Exit Do End If Loop End Sub Sub Mnozenie() Dim stdin As StreamReader, stdout As System.IO.StreamWriter Dim s As String stdin = New StreamReader(System.Console.OpenStandardInput()) stdout = New StreamWriter(System.Console.OpenStandardOutput()) Do s = stdin.readline() If Not s Is Nothing Then Console.WriteLine(CStr(CInt(s) * 2)) Else Exit Do End If Loop End Sub End Module