Skip to content

4. Powszechnie używane klasy .NET

Poniżej przedstawiamy kilka interesujących klas platformy .NET, które mogą zainteresować nawet początkujących użytkowników. Najpierw pokażemy, jak uzyskać informacje na temat setek dostępnych klas.

4.1. Szukanie pomocy w SDK.NET

4.1.1. wincv

Jeśli zainstalowano tylko SDK, a nie Visual Studio.NET, można skorzystać z programu wincv.exe, który zazwyczaj znajduje się w drzewie katalogów zestawu SDK, na przykład w lokalizacji C:\Program Files\Microsoft Visual Studio.NET 2003\SDK\v1.1. Po uruchomieniu tego narzędzia wyświetla się następujący interfejs:

W polu (1) wpisujemy nazwę żądanej klasy. Spowoduje to wyświetlenie w polu (2) różnych możliwych tematów. Wybieramy odpowiedni i otrzymujemy wynik w polu (3) – w tym przypadku klasę HashTable. Ta metoda sprawdza się, jeśli znamy nazwę klasy, której szukamy. Jeśli chcemy przejrzeć listę możliwości oferowanych przez platformę .NET, można skorzystać z pliku HTML StartHere.htm, który również znajduje się bezpośrednio w folderze instalacyjnym SSK.Net, na przykład C:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.

Image

Aby uzyskać ogólny przegląd klas .NET Framework SDK Documentation, należy kliknąć poniższy link:

Image

W tym miejscu należy kliknąć link [Bibliothèque de classes]. Znajduje się tam lista wszystkich klas .NET:

Image

Kliknijmy na przykład link System.Collections. Ta przestrzeń nazw zawiera różne klasy implementujące kolekcje, w tym klasę HashTable:

Image

Przejdźmy do poniższego linku HashTable:

Image

Otrzymujemy następującą stronę:

Image

Zwróćmy uwagę na położenie kursora poniżej. Wskazuje on na link umożliwiający wybranie żądanego języka, w tym przypadku Visual Basic.

Znajduje się tam prototyp klasy oraz przykłady jej zastosowania. Kliknijmy poniższy link [Hashtable, membres]:

Image

Otrzymujemy pełny opis klasy:

Image

Ta metoda jest najlepsza do zapoznania się z SDK i jego klasami. Narzędzie WinCV okazuje się przydatne, gdy znamy już nieco daną klasę, ale zapomnieliśmy niektórych jej elementów składowych. WinCV pozwala wówczas szybko odnaleźć klasę i jej elementy składowe.

4.2. Szukanie pomocy dotyczącej klas za pomocą VS.NET

Poniżej przedstawiamy kilka wskazówek dotyczących wyszukiwania pomocy w programie Visual Studio.NET

4.2.1. Opcja Pomoc

Wybierzmy opcję [?] z menu.

Image

Pojawi się następujące okno:

Image

Z listy rozwijanej można wybrać filtr pomocy. W tym przypadku wybierzemy filtr [Visual Basic].

Image

Przydatne są dwie instrukcje:

  • pomoc dotycząca samego języka VB.NET (składnia)
  • pomoc dotycząca elementów classes.NET, które można wykorzystać w języku VB.NET

Pomoc dotycząca języka VB.NET jest dostępna poprzez [Visual Studio.NET/Visual Basic et Visual C#/Reference/Visual Basic]:

Image

Wyświetla się następująca strona pomocy:

Image

Stąd, dzięki różnym podsekcjom, możemy uzyskać pomoc dotyczącą różnych zagadnień związanych z VB.NET. Zwróćmy uwagę na samouczki dotyczące VB.NET:

Image

Aby uzyskać dostęp do różnych klas platformy .NET, należy wybrać pomoc [Visual Studio.NET/.NET Framework].

Image

Zwróćmy szczególną uwagę na sekcję [Référence/Bibliothèque de classes]:

Image

Załóżmy, że interesuje nas klasa [ArrayList]. Znajduje się ona w przestrzeni nazw [System.Collections]. Należy o tym wiedzieć, w przeciwnym razie lepiej skorzystać z metody wyszukiwania opisanej poniżej. Otrzymujemy następującą pomoc:

Image

Link [ArrayList, classe] zawiera ogólny opis klasy:

Image

Tego typu strona istnieje dla wszystkich klas. Zawiera ona podsumowanie klasy wraz z przykładami. Aby zapoznać się z opisem elementów tej klasy, należy kliknąć link [ArrayList, membres]:

Image

4.2.2. Pomoc/Indeks

Image

Opcja [Aide/index] pozwala na wyszukiwanie pomocy bardziej ukierunkowanej niż poprzednia. Wystarczy wpisać szukane słowo kluczowe:

Image

Zaletą tej metody w porównaniu z poprzednią jest to, że nie trzeba wiedzieć, gdzie w systemie pomocy znajduje się poszukiwana informacja. Jest to prawdopodobnie metoda preferowana w przypadku wyszukiwania ukierunkowanego, podczas gdy druga metoda jest bardziej odpowiednia do zapoznania się z całą zawartością pomocy.

4.3. Klasa String

Klasa String posiada wiele właściwości i metod. Oto kilka z nich:

Public ReadOnly Property Length As Integer
liczba znaków w ciągu
Public Default ReadOnly Property 
Chars(ByVal index As Integer) As Char
domyślna właściwość indeksowana. [String].Chars(i) to znak o numerze i w ciągu
Public Function EndsWith(ByVal value As String) 
As Boolean
zwraca wartość true, jeśli ciąg kończy się wartością
Public Function StartsWith(ByVal value As String) 
As Boolean
zwraca wartość logiczną „prawda”, jeśli ciąg znaków zaczyna się od wartości
Overloads Public Function Equals(ByVal value 
As String) As Boolean
zwraca wartość logiczną „prawda”, jeśli ciąg znaków jest równy wartości
Overloads Public Function IndexOf(ByVal value 
As String) As Integer
zwraca pierwszą pozycję ciągu w ciągu „value” – wyszukiwanie rozpoczyna się od znaku nr 0
Overloads Public Function IndexOf(ByVal value 
As String,ByVal startIndex As Integer) As Integer
zwraca pierwszą pozycję w ciągu znaków o wartości „value” – wyszukiwanie rozpoczyna się od znaku nr startIndex
Overloads Public Shared Function 
Join(ByVal separator As String,ByVal value() 
As String) As String
metoda klasy – zwraca ciąg znaków będący wynikiem połączenia wartości z tablicy value z separatorem separator
Overloads Public Function Replace(ByVal oldChar 
As Char,ByVal newChar As Char) As String
zwraca kopię bieżącego ciągu znaków, w której znak oldChar został zastąpiony znakiem newChar
Overloads Public Function Split(ByVal ParamArray
separator() As Char) As String()
ciąg znaków jest traktowany jako sekwencja pól oddzielonych znakami zawartymi w tablicy separator. Wynikiem jest tablica tych pól
Overloads Public Function Substring(
ByVal startIndex As Integer,ByVal length As Integer) 
As String
podciąg bieżącego ciągu, zaczynający się w pozycji startIndex i zawierający length znaków
Overloads Public Function ToLower() 
As String
zamienia bieżący ciąg na małe litery
Overloads Public Function ToUpper() 
As String
zamienia bieżący ciąg znaków na wielkie litery
Overloads Public Function Trim() 
As String
usuwa spacje na początku i na końcu bieżącego ciągu znaków

Ciąg znaków C można traktować jako tablicę znaków. Zatem

  • C.Chars(i) oznacza znak i z C
  • C.Length to liczba znaków w ciągu C

Rozważmy następujący przykład:


' opcje
Option Strict On
Option Explicit On 

' przestrzenie nazw
Imports System

Module test
    Sub Main()
        Dim uneChaine As String = "l'oiseau vole au-dessus des nuages"
        affiche("uneChaine=" + uneChaine)
        affiche("uneChaine.Length=" & uneChaine.Length)
        affiche("chaine[10]=" + uneChaine.Chars(10))
        affiche("uneChaine.IndexOf(""vole"")=" & uneChaine.IndexOf("vole"))
        affiche("uneChaine.IndexOf(""x"")=" & uneChaine.IndexOf("x"))
        affiche("uneChaine.LastIndexOf('a')=" & uneChaine.LastIndexOf("a"c))
        affiche("uneChaine.LastIndexOf('x')=" & uneChaine.LastIndexOf("x"c))
        affiche("uneChaine.Substring(4,7)=" + uneChaine.Substring(4, 7))
        affiche("uneChaine.ToUpper()=" + uneChaine.ToUpper())
        affiche("uneChaine.ToLower()=" + uneChaine.ToLower())
        affiche("uneChaine.Replace('a','A')=" + uneChaine.Replace("a"c, "A"c))
        Dim champs As String() = uneChaine.Split(Nothing)
        Dim i As Integer
        For i = 0 To champs.Length - 1
            affiche("champs[" & i & "]=[" & champs(i) & "]")
        Next i
        affiche("Join("":"",champs)=" + System.String.Join(":", champs))
        affiche("(""  abc  "").Trim()=[" + "  abc  ".Trim() + "]")
    End Sub

    ' wyświetla
    Sub affiche(ByVal msg As [String])
        ' wyświetla komunikat
        Console.Out.WriteLine(msg)
    End Sub
End Module

Wykonanie daje następujące wyniki:

dos>vbc string1.vb

dos>string1
uneChaine=l'oiseau vole au-dessus des nuages
uneChaine.Length=34
chaine[10]=o
uneChaine.IndexOf("vole")=9
uneChaine.IndexOf("x")=-1
uneChaine.LastIndexOf('a')=30
uneChaine.LastIndexOf('x')=-1
uneChaine.Substring(4,7)=seau vo
uneChaine.ToUpper()=L'OISEAU VOLE AU-DESSUS DES NUAGES
uneChaine.ToLower()=l'oiseau vole au-dessus des nuages
uneChaine.Replace('a','A')=l'oiseAu vole Au-dessus des nuAges
champs[0]=[l'oiseau]
champs[1]=[vole]
champs[2]=[au-dessus]
champs[3]=[des]
champs[4]=[nuages]
Join(":",champs)=l'oiseau:vole:au-dessus:des:nuages
("  abc  ").Trim()=[abc]

Rozważmy nowy przykład:


' opcje
Option Strict On
Option Explicit On 

' przestrzenie nazw
Imports System

Module string2
    Sub Main()
        ' analizowana linia
        Dim ligne As String = "un:deux::trois:"
        ' separatory pól
        Dim séparateurs() As Char = {":"c}
        ' podział
        Dim champs As String() = ligne.Split(séparateurs)
        Dim i As Integer
        For i = 0 To champs.Length - 1
            Console.Out.WriteLine(("Champs[" & i & "]=" & champs(i)))
        Next i
        ' łączenie
        Console.Out.WriteLine(("join=[" + System.String.Join(":", champs) + "]"))
    End Sub
End Module

oraz wyniki wykonania:

Champs[0]=un
Champs[1]=deux
Champs[2]=
Champs[3]=trois
Champs[4]=
join=[un:deux::trois:]

Metoda Split klasy String pozwala umieścić pola ciągu znaków w tablicy. Definicja tej metody jest następująca:

Overloads Public Function Split(ByVal ParamArray separator() As Char) As String()
separator
tablica znaków. Znaki te odpowiadają znakom używanym do oddzielania pól w ciągu znaków. Jeśli więc ciąg znaków to [champ1, champ2, champ3], można użyć separatora: separator=new char() {","c}. Jeśli separatorem jest ciąg spacji, należy użyć separator=nothing.
résultat
tablica ciągów znaków, w której każdy element jest polem ciągu znaków.

Metoda Join jest metodą statyczną klasy String:

Overloads Public Shared Function Join(ByVal separator As String,ByVal value() As String) As String
value
tablica ciągów znaków
separator
ciąg znaków, który będzie służył jako separator pól
résultat
ciąg znaków utworzony przez połączenie elementów tablicy value, oddzielonych ciągiem separator.

4.4. Klasa Array

Klasa Array implementuje tablicę. W naszym przykładzie wykorzystamy następujące właściwości i metody:

Public ReadOnly Property Length As Integer
właściwość – liczba elementów tablicy
Overloads Public Shared Function BinarySearch
(ByVal array As Array,ByVal index As Integer,
ByVal length As Integer,ByVal value As Object) As Integer
metoda klasy – zwraca pozycję value w posortowanej tablicy array – wyszukuje od pozycji index i z length elementami
Overloads Public Shared Sub Copy(ByVal sourceArray 
As Array,ByVal destinationArray As Array,
ByVal length As Integer)
metoda klasy – kopiuje elementy z length do destinationArray z sourceArray – przy tej okazji tworzony jest destinationArray
Overloads Public Shared Sub Sort(ByVal array As Array)
metoda klasy – sortuje tablicę array – musi ona zawierać typ danych posiadający domyślną funkcję porządkowania (ciągi znaków, liczby).

Poniższy program ilustruje wykorzystanie klasy Array:


' opcje
Option Strict On
Option Explicit On 

' przestrzenie nazw
Imports System

Module test
    Sub Main()
        ' odczyt elementów tablicy wprowadzonych z klawiatury
        Dim terminé As [Boolean] = False
        Dim i As Integer = 0
        Dim éléments1 As Double() = Nothing
        Dim éléments2 As Double() = Nothing
        Dim élément As Double = 0
        Dim réponse As String = Nothing
        Dim erreur As [Boolean] = False

        While Not terminé
            ' pytanie
            Console.Out.Write(("Elément (réel) " & i & " du tableau (rien pour terminer) : "))
            ' odczyt odpowiedzi
            réponse = Console.ReadLine().Trim()
            ' zakończenie wprowadzania danych, jeśli ciąg znaków jest pusty
            If réponse.Equals("") Then
                Exit While
            End If
            ' sprawdzanie wprowadzonych danych
            Try
                élément = [Double].Parse(réponse)
                erreur = False
            Catch
                Console.Error.WriteLine("Saisie incorrecte, recommencez")
                erreur = True
            End Try
            ' jeśli nie ma błędu
            If Not erreur Then
                ' nowa tablica na nowy element
                éléments2 = New Double(i) {}
                ' kopiowanie starej tablicy do nowej tablicy
                If i <> 0 Then
                    Array.Copy(éléments1, éléments2, i)
                End If
                ' nowa tablica staje się starą tablicą
                éléments1 = éléments2
                ' nowa tablica nie jest już potrzebna
                éléments2 = Nothing
                ' wstawienie nowego elementu
                éléments1(i) = élément
                ' jeden element więcej w tabeli
                i += 1
            End If
        End While
        ' wyświetlenie tabeli nieposortowanej
        System.Console.Out.WriteLine("Tableau non trié")
        For i = 0 To éléments1.Length - 1
            Console.Out.WriteLine(("éléments[" & i & "]=" & éléments1(i)))
        Next i
        ' sortowanie tabeli
        System.Array.Sort(éléments1)
        ' wyświetlanie posortowanej tabeli
        System.Console.Out.WriteLine("Tableau trié")
        For i = 0 To éléments1.Length - 1
            Console.Out.WriteLine(("éléments[" & i & "]=" & éléments1(i)))
        Next i
        ' Wyszukiwanie
        While Not terminé
            ' pytanie
            Console.Out.Write("Elément cherché (rien pour arrêter) : ")
            ' odczyt i weryfikacja odpowiedzi
            réponse = Console.ReadLine().Trim()
            ' koniec?
            If réponse.Equals("") Then
                Exit While
            End If
            ' weryfikacja
            Try
                élément = [Double].Parse(réponse)
                erreur = False
            Catch
                Console.Error.WriteLine("Saisie incorrecte, recommencez")
                erreur = True
            End Try
            ' jeśli nie ma błędu
            If Not erreur Then
                ' szukamy elementu w posortowanej tablicy
                i = System.Array.BinarySearch(éléments1, 0, éléments1.Length, élément)
                ' Wyświetlanie odpowiedzi
                If i >= 0 Then
                    Console.Out.WriteLine(("Trouvé en position " & i))
                Else
                    Console.Out.WriteLine("Pas dans le tableau")
                End If
            End If
        End While
    End Sub
End Module

Wyniki wyświetlane na ekranie są następujące:

Elément (réel) 0 du tableau (rien pour terminer) : 10,4
Elément (réel) 1 du tableau (rien pour terminer) : 5,2
Elément (réel) 2 du tableau (rien pour terminer) : 8,7
Elément (réel) 3 du tableau (rien pour terminer) : 3,6
Elément (réel) 4 du tableau (rien pour terminer) :
Tableau non trié
éléments[0]=10,4
éléments[1]=5,2
éléments[2]=8,7
éléments[3]=3,6
Tableau trié
éléments[0]=3,6
éléments[1]=5,2
éléments[2]=8,7
éléments[3]=10,4
Elément cherché (rien pour arrêter) : 8,7
Trouvé en position 2
Elément cherché (rien pour arrêter) : 11
Pas dans le tableau
Elément cherché (rien pour arrêter) : a
Saisie incorrecte, recommencez
Elément cherché (rien pour arrêter) :

W pierwszej części programu tworzona jest tablica na podstawie danych liczbowych wprowadzonych z klawiatury. Nie można z góry określić rozmiaru tablicy, ponieważ nie wiadomo, ile elementów wprowadzi użytkownik. Pracujemy zatem z dwiema tablicami: éléments1 i éléments2.


                ' nowa tablica na nowy element
                éléments2 = New Double(i) {}
                ' kopiowanie starej tablicy do nowej tablicy
                If i <> 0 Then
                    Array.Copy(éléments1, éléments2, i)
                End If
                ' nowa tabela staje się starą tabelą
                éléments1 = éléments2
                ' nowa tabela nie jest już potrzebna
                éléments2 = Nothing
                ' wstawienie nowego elementu
                éléments1(i) = élément
                ' jeden element więcej w tabeli
                i += 1

Tablica éléments1 zawiera aktualnie wprowadzone wartości. Gdy użytkownik dodaje nową wartość, tworzy się tablicę éléments2, która ma o jeden element więcej niż éléments1, kopiuje się zawartość tabeli éléments1 do tabeli éléments2 (Array.Copy), ustawia się „odwołanie” tablicy éléments1 do tablicy éléments2, a na koniec dodaje się nowy element do tablicy éléments1. Jest to złożony proces, który można uprościć, jeśli zamiast tablicy o stałym rozmiarze (Array) użyje się tablicy o zmiennym rozmiarze (ArrayList).

Tablica jest sortowana za pomocą metody Array.Sort. Metodę tę można wywołać z różnymi parametrami określającymi reguły sortowania. Bez parametrów domyślnie wykonywane jest sortowanie w porządku rosnącym. Wreszcie metoda Array.BinarySearch umożliwia wyszukiwanie elementu w posortowanej tablicy.

4.5. Klasa ArrayList

Klasa ArrayList umożliwia implementację tablic o zmiennej wielkości w trakcie wykonywania programu, czego nie pozwala poprzednia klasa Array. Oto niektóre z typowych właściwości i metod:

Public Sub New()
tworzy pustą listę
Public Overridable ReadOnly Property Count 
As Integer  Implements ICollection.Count
liczba elementów w kolekcji
Public Overridable Function Add(ByVal value 
As Object) As Integer Implements IList.Add
dodaje obiekt „value” na końcu kolekcji
Public Overridable Sub Clear() 
Implements IList.Clear
usuwa listę
Overloads Public Overridable Function 
IndexOf(ByVal value As Object) As Integer 
Implements IList.IndexOf
indeks elementu „value” na liście lub -1, jeśli nie istnieje
Overloads Public Overridable Function 
IndexOf(ByVal value As Object, ByVal startIndex 
As Integer) As Integer
to samo, ale wyszukiwanie rozpoczyna się od elementu o numerze startIndex
Overloads Public Overridable Function 
LastIndexOf(ByVal value As Object) As Integer
to samo, ale zwraca indeks ostatniego wystąpienia wartości na liście
Overloads Public Overridable Function 
LastIndexOf(ByVal value As Object, ByVal startIndex As Integer) As Integer
to samo, ale wyszukiwanie rozpoczyna się od elementu nr startIndex
Public Overridable Sub Remove( ByVal obj As Object)
Implements IList.Remove
usuń obiekt obj, jeśli występuje na liście
Public Overridable Sub RemoveAt(ByVal index As Integer) 
Implements IList.RemoveAt
usuwa element o indeksie z listy
Overloads Public Overridable Function 
BinarySearch(ByVal value As Object) As Integer
zwraca pozycję obiektu „value” na liście lub -1, jeśli go tam nie ma. Lista musi być posortowana
Overloads Public Overridable Sub Sort()
sortuje listę. Lista ta musi zawierać obiekty o z góry zdefiniowanej relacji porządkowej (ciągi znaków, liczby)
Overloads Public Overridable Sub 
Sort(ByVal comparer As IComparer)
sortuje listę zgodnie z relacją porządkową ustaloną przez funkcję porównującą

Wróćmy do przykładu z obiektami typu Array i przetwórzmy go z wykorzystaniem obiektów typu ArrayList:


' opcje
Option Strict On
Option Explicit On 

' przestrzenie nazw
Imports System
Imports System.Collections

Module test
    Sub Main()
        ' odczyt elementów tablicy wpisanych z klawiatury
        Dim terminé As [Boolean] = False
        Dim i As Integer = 0
        Dim éléments As New ArrayList
        Dim élément As Double = 0
        Dim réponse As String = Nothing
        Dim erreur As [Boolean] = False

        While Not terminé
            ' pytanie
            Console.Out.Write(("Elément (réel) " & i & " du tableau (rien pour terminer) : "))
            ' odczyt odpowiedzi
            réponse = Console.ReadLine().Trim()
            ' zakończenie wprowadzania danych, jeśli ciąg znaków jest pusty
            If réponse.Equals("") Then
                Exit While
            End If
            ' sprawdzanie wprowadzonych danych
            Try
                élément = Double.Parse(réponse)
                erreur = False
            Catch
                Console.Error.WriteLine("Saisie incorrecte, recommencez")
                erreur = True
            End Try
            ' jeśli nie ma błędu
            If Not erreur Then
                ' kolejny element w tablicy
                éléments.Add(élément)
            End If
        End While
        ' wyświetlenie tablicy nieposortowanej
        System.Console.Out.WriteLine("Tableau non trié")
        For i = 0 To éléments.Count - 1
            Console.Out.WriteLine(("éléments[" & i & "]=" & éléments(i).ToString))
        Next i        ' tri du tableau
        éléments.Sort()
        ' wyświetlanie posortowanej tablicy
        System.Console.Out.WriteLine("Tableau trié")
        For i = 0 To éléments.Count - 1
            Console.Out.WriteLine(("éléments[" & i & "]=" & éléments(i).ToString))
        Next i
        ' Wyszukiwanie
        While Not terminé
            ' pytanie
            Console.Out.Write("Elément cherché (rien pour arrêter) : ")
            ' odczyt i weryfikacja odpowiedzi
            réponse = Console.ReadLine().Trim()
            ' koniec?
            If réponse.Equals("") Then
                Exit While
            End If
            ' weryfikacja
            Try
                élément = [Double].Parse(réponse)
                erreur = False
            Catch
                Console.Error.WriteLine("Saisie incorrecte, recommencez")
                erreur = True
            End Try
            ' jeśli nie ma błędu
            If Not erreur Then
                ' szukamy elementu w posortowanej tablicy
                i = éléments.BinarySearch(élément)
                ' Wyświetlanie odpowiedzi
                If i >= 0 Then
                    Console.Out.WriteLine(("Trouvé en position " & i))
                Else
                    Console.Out.WriteLine("Pas dans le tableau")
                End If
            End If
        End While
    End Sub
End Module

Wyniki wykonania są następujące:

Elément (réel) 0 du tableau (rien pour terminer) : 10,4
Elément (réel) 0 du tableau (rien pour terminer) : 5,2
Elément (réel) 0 du tableau (rien pour terminer) : a
Saisie incorrecte, recommencez
Elément (réel) 0 du tableau (rien pour terminer) : 3,7
Elément (réel) 0 du tableau (rien pour terminer) : 15
Elément (réel) 0 du tableau (rien pour terminer) :
Tableau non trié
éléments[0]=10,4
éléments[1]=5,2
éléments[2]=3,7
éléments[3]=15
Tableau trié
éléments[0]=3,7
éléments[1]=5,2
éléments[2]=10,4
éléments[3]=15
Elément cherché (rien pour arrêter) : a
Saisie incorrecte, recommencez
Elément cherché (rien pour arrêter) : 15
Trouvé en position 3
Elément cherché (rien pour arrêter) : 1
Pas dans le tableau
Elément cherché (rien pour arrêter) :

4.6. Klasa Hashtable

Klasa Hashtable umożliwia implementację słownika. Słownik można postrzegać jako tablicę o dwóch kolumnach:

Klucze są unikalne, c.a.d, co oznacza, że nie mogą występować dwa identyczne klucze. Główne metody i właściwości klasy Hashtable są następujące:

Public Sub New()
tworzy pusty słownik
Public Overridable Sub Add(ByVal key As
Object,ByVal value As Object) 
Implements IDictionary.Add
dodaje wiersz (key, value) do słownika, gdzie key i value są odwołaniami do obiektów.
Public Overridable Sub Remove
(ByVal key As Object) Implements IDictionary.Remove
usuwa z słownika wiersz o kluczu=key
Public Overridable Sub Clear() 
Implements IDictionary.Clear
opróżnia słownik
Public Overridable Function ContainsKey
(ByVal key As Object) As Boolean
zwraca wartość true, jeśli klucz key znajduje się w słowniku.
Public Overridable Function 
ContainsValue(ByVal value As Object) As Boolean
zwraca wartość true, jeśli wartość value znajduje się w słowniku.
Public Overridable ReadOnly Property 
Count As Integer  Implements ICollection.Count
właściwość: liczba elementów słownika (klucz, wartość)
Public Overridable ReadOnly Property 
Keys As ICollection Implements IDictionary.Keys
właściwość: zbiór kluczy słownika
Public Overridable ReadOnly Property 
Values As ICollection Implements IDictionary.Values
właściwość: zbiór wartości słownika
Public Overridable Default Property 
Item(ByVal key As Object) As Object  Implements IDictionary.Item
właściwość indeksowana: pozwala sprawdzić lub ustawić wartość powiązaną z kluczem key

Rozważmy następujący przykładowy program:


' opcje
Option Strict On
Option Explicit On 

' przestrzenie nazw
Imports System
Imports System.Collections

Module test
    Sub Main()
        Dim liste() As [String] = {"jean:20", "paul:18", "mélanie:10", "violette:15"}
        Dim i As Integer
        Dim champs As [String]() = Nothing
        Dim séparateurs() As Char = {":"c}
        ' wypełnianie słownika
        Dim dico As New Hashtable
        For i = 0 To liste.Length - 1
            champs = liste(i).Split(séparateurs)
            dico.Add(champs(0), champs(1))
        Next i
        ' liczba elementów w słowniku
        Console.Out.WriteLine(("Le dictionnaire a " & dico.Count & " éléments"))
        ' lista kluczy
        Console.Out.WriteLine("[Liste des clés]")
        Dim clés As IEnumerator = dico.Keys.GetEnumerator()
        While clés.MoveNext()
            Console.Out.WriteLine(clés.Current)
        End While
        ' lista wartości
        Console.Out.WriteLine("[Liste des valeurs]")
        Dim valeurs As IEnumerator = dico.Values.GetEnumerator()
        While valeurs.MoveNext()
            Console.Out.WriteLine(valeurs.Current)
        End While
        ' lista kluczy i wartości
        Console.Out.WriteLine("[Liste des clés & valeurs]")
        clés.Reset()
        While clés.MoveNext()
            Console.Out.WriteLine(("clé=" & clés.Current.ToString & " valeur=" & dico(clés.Current).ToString))
        End While
        ' usuwamy klucz „paul”
        Console.Out.WriteLine("[Suppression d'une clé]")
        dico.Remove("paul")
        ' lista kluczy i wartości
        Console.Out.WriteLine("[Liste des clés & valeurs]")
        clés = dico.Keys.GetEnumerator()
        While clés.MoveNext()
            Console.Out.WriteLine(("clé=" & clés.Current.ToString & " valeur=" & dico(clés.Current).ToString))
        End While

        ' wyszukiwanie w słowniku
        Dim nomCherché As [String] = Nothing
        Console.Out.Write("Nom recherché (rien pour arrêter) : ")
        nomCherché = Console.ReadLine().Trim()
        Dim value As [Object] = Nothing
        While Not nomCherché.Equals("")
            If dico.ContainsKey(nomCherché) Then
                value = dico(nomCherché)
                Console.Out.WriteLine((nomCherché + "," + CType(value, [String])))
            Else
                Console.Out.WriteLine(("Nom " + nomCherché + " inconnu"))
            End If
            ' kolejne wyszukiwanie
            Console.Out.Write("Nom recherché (rien pour arrêter) : ")
            nomCherché = Console.ReadLine().Trim()
        End While
    End Sub
End Module

Wyniki wykonania są następujące:

Le dictionnaire a 4 éléments
[Liste des clés]
mélanie
paul
violette
jean
[Liste des valeurs]
10
18
15
20
[Liste des clés & valeurs]
clé=mélanie valeur=10
clé=paul valeur=18
clé=violette valeur=15
clé=jean valeur=20
[Suppression d'une clé]
[Liste des clés & valeurs]
clé=mélanie valeur=10
clé=violette valeur=15
clé=jean valeur=20
Nom recherché (rien pour arrêter) : paul
Nom paul inconnu
Nom recherché (rien pour arrêter) : mélanie
mélanie,10
Nom recherché (rien pour arrêter) :

Program wykorzystuje również obiekt IEnumerator do przeglądania zbiorów kluczy i wartości słownika typu ICollection (patrz powyżej właściwości Keys i Values). Kolekcja to zbiór obiektów, po którym można przeglądać. Interfejs ICollection jest zdefiniowany w następujący sposób:

Image

Właściwość Count pozwala nam poznać liczbę elementów w kolekcji. Interfejs ICollection wywodzi się z interfejsu IEnumerable:

Image

Ten interfejs posiada tylko jedną metodę GetEnumerator, która pozwala uzyskać obiekt typu IEnumerator:

Image

Metoda GetEnumerator() kolekcji ICollection pozwala nam przeglądać kolekcję za pomocą następujących metod:

MoveNext
przechodzi do następnego elementu kolekcji. Zwraca wartość true, jeśli ten element istnieje, w przeciwnym razie false. Pierwsza metoda MoveNext przechodzi do pierwszego elementu. „Bieżący” element kolekcji jest wówczas dostępny we właściwości Current iteratora
Current
właściwość: bieżący element kolekcji
Reset
przesuwa wskaźnik na początek kolekcji, c.a.d, przed pierwszy element.

Struktura iteracji nad elementami kolekcji (ICollection) w języku C wygląda zatem następująco:

' zdefiniowanie kolekcji
dim C as ICollection C=...
' pobierz wskaźnik tej kolekcji
dim itérateur as IEnumerator=C.GetEnumerator();
' przeglądanie kolekcji za pomocą tego indeksatora
while(itérateur.MoveNext())
    ' mamy bieżący element
     ' wykorzystać itérateur.Current
end while

4.7. Klasa StreamReader

Klasa StreamReader umożliwia odczytanie zawartości pliku. Oto niektóre z jej właściwości i metod:

Public Sub New(ByVal path As String)
otwiera strumień z pliku path. Jeśli plik nie istnieje, zgłaszany jest wyjątek
Overrides Public Sub Close()
zamyka strumień
Overrides Public Function ReadLine() As String
odczytuje wiersz z otwartego strumienia
Overrides Public Function ReadToEnd() As String
odczytuje pozostałą część strumienia od bieżącej pozycji

Oto przykład:


' opcje
Option Strict On
Option Explicit On 

' przestrzenie nazw
Imports System
Imports System.Collections
Imports System.IO

Module test
    Sub Main()
        Dim ligne As String = Nothing
        Dim fluxInfos As StreamReader = Nothing
        ' odczyt zawartości pliku
        Try
            fluxInfos = New StreamReader("infos.txt")
            ligne = fluxInfos.ReadLine()
            While Not (ligne Is Nothing)
                System.Console.Out.WriteLine(ligne)
                ligne = fluxInfos.ReadLine()
            End While
        Catch e As Exception
            System.Console.Error.WriteLine("L'erreur suivante s'est produite : " & e.ToString)
        Finally
            Try
                fluxInfos.Close()
            Catch
            End Try
        End Try
    End Sub
End Module

oraz wyniki jego wykonania:

dos>more infos.txt
12620:0:0
13190:0,05:631
15640:0,1:1290,5
24740:0,15:2072,5
31810:0,2:3309,5
39970:0,25:4900
48360:0,3:6898,5
55790:0,35:9316,5
92970:0,4:12106
127860:0,45:16754,5
151250:0,5:23147,5
172040:0,55:30710
195000:0,6:39312
0:0,65:49062

dos>file1
12620:0:0
13190:0,05:631
15640:0,1:1290,5
24740:0,15:2072,5
31810:0,2:3309,5
39970:0,25:4900
48360:0,3:6898,5
55790:0,35:9316,5
92970:0,4:12106
127860:0,45:16754,5
151250:0,5:23147,5
172040:0,55:30710
195000:0,6:39312
0:0,65:49062

4.8. Klasa StreamWriter

Klasa StreamWriter umożliwia zapisywanie danych do pliku. Oto niektóre z jej właściwości i metod:

Public Sub New(ByVal path As String)
otwiera strumień zapisu z pliku path. Jeśli nie da się go utworzyć, zgłaszany jest wyjątek
Public Overridable Property AutoFlush 
As Boolean
jeśli wartość jest równa true, zapis do strumienia odbywa się bez buforowania; w przeciwnym razie zapis do strumienia nie następuje natychmiast: najpierw dane są zapisywane do bufora, a dopiero gdy bufor się zapełni, trafiają do strumienia. Domyślnie używany jest tryb buforowany. Nadaje się on dobrze do strumieni plików, ale zazwyczaj nie do strumieni sieciowych.
Public Overridable Property NewLine 
As String
aby ustawić lub sprawdzić znak końca linii, który ma być używany przez metodę WriteLine
Overrides Public Sub Close()
zamyka strumień
Overloads Public Overridable Sub 
WriteLine(ByVal value As String)
zapisuje wiersz w strumieniu zapisu
Overrides Public Sub Flush()
zapisuje zawartość bufora do strumienia

Rozważmy następujący przykład:


' opcje
Option Strict On
Option Explicit On 

' przestrzenie nazw
Imports System
Imports System.Collections
Imports System.IO

Module test
    Sub Main()
        Dim ligne As String = Nothing        ' une ligne de texte
        Dim fluxInfos As StreamWriter = Nothing        ' le fichier texte
        Try
            ' tworzenie pliku tekstowego
            fluxInfos = New StreamWriter("infos.txt")
            ' odczyt wiersza wprowadzonego z klawiatury
            Console.Out.Write("ligne (rien pour arrêter) : ")
            ligne = Console.In.ReadLine().Trim()
            ' pętla, dopóki wprowadzony wiersz nie jest pusty
            While ligne <> ""
                ' zapis wiersza do pliku tekstowego
                fluxInfos.WriteLine(ligne)
                ' odczyt nowego wiersza z klawiatury
                Console.Out.Write("ligne (rien pour arrêter) : ")
                ligne = Console.In.ReadLine().Trim()
            End While
        Catch e As Exception
            System.Console.Error.WriteLine("L'erreur suivante s'est produite : " & e.ToString)
        Finally
            ' zamknięcie pliku
            Try
                fluxInfos.Close()
            Catch
            End Try
        End Try
    End Sub
End Module

oraz wyniki wykonania:

dos>file2
ligne (rien pour arrêter) : ligne1
ligne (rien pour arrêter) : ligne2
ligne (rien pour arrêter) : ligne3
ligne (rien pour arrêter) :

dos>more infos.txt
ligne1
ligne2
ligne3

4.9. Klasa Regex

Klasa Regex umożliwia stosowanie wyrażeń regularnych. Pozwalają one na sprawdzanie formatu ciągu znaków. Dzięki temu można zweryfikować, czy ciąg znaków reprezentujący datę ma format dd/mm/rr. W tym celu wykorzystuje się wzorzec i porównuje się ciąg znaków z tym wzorcem. W tym przykładzie znaki j, m i a muszą być cyframi. Szablon prawidłowego formatu daty to zatem „\d\d/\d\d/\d\d”, gdzie symbol \d oznacza cyfrę. Symbole, których można używać w szablonie, są następujące (dokumentacja Microsoft):

 
Opis
\ 
Oznacza następny znak jako znak specjalny lub dosłowny. Na przykład „n” odpowiada znakowi „n”. „\n” odpowiada znakowi nowego wiersza. Sekwencja „\\” odpowiada znakowi „\”, natomiast „\(" odpowiada znakowi „(”.
^ 
Odpowiada początkowi wprowadzania tekstu.
$ 
Odpowiada końcowi wpisu.
* 
Odpowiada poprzedniemu znakowi zero lub więcej razy. Tak więc „zo*” odpowiada „z” lub „zoo”.
+ 
Odpowiada poprzedniemu znakowi jeden lub więcej razy. Tak więc „zo+” odpowiada „zoo”, ale nie odpowiada „z”.
? 
Odpowiada poprzedniemu znakowi zero lub jeden raz. Na przykład „a?ve?” odpowiada „ve” w słowie „lever”.
.
Odpowiada dowolnemu pojedynczemu znakowi, z wyjątkiem znaku nowej linii.

(modèle) 
Wyszukaj modèle i zapamiętaj dopasowanie. Odpowiedni podciąg można wyodrębnić z uzyskanej kolekcji Matches za pomocą elementu [0]...[n]. Aby znaleźć dopasowania zawierające znaki w nawiasach ( ), należy użyć znaków „\(" lub „\)”.

x|y
Odpowiada albo x, albo y. Na przykład „z|foot" correspond à "z" ou à "foot". "(z|f)oo” odpowiada „zoo” lub „foo”.

{n}
n to nieujemna liczba całkowita. Odpowiada dokładnie n pomnożonemu przez znak. Na przykład „o{2}” nie odpowiada literze „o” w słowie „Bob”, ale dwóm pierwszym literom „o” w słowie „fooooot”.

{n,} 
n to liczba całkowita nieujemna. Odpowiada co najmniej n-krotności danego znaku. Na przykład „o{2,}” nie odpowiada literze „o” w słowie „Bob”, ale wszystkim literom „o” w słowie „fooooot”. „o{1,}” jest równoważne „o+”, a „o{0,}” jest równoważne „o*”.

{n,m} 
m i n to liczby całkowite nieujemne. Odpowiada co najmniej n i co najwyżej m wystąpieniom tego znaku. Na przykład „o{1,3}” odpowiada trzem pierwszym literom „o” w słowie „foooooot”, a „o{0,1}” jest równoważne „o?”.

[xyz] 
Zestaw znaków. Odpowiada jednemu z podanych znaków. Na przykład „[abc]” odpowiada literze „a” w słowie „plat”.

[^xyz] 
Zbiór znaków ujemnych. Odpowiada każdemu znakowi, który nie został wymieniony. Na przykład „[^abc]” odpowiada literze „p” w słowie „plat”.

[a-z] 
Zakres znaków. Odpowiada dowolnemu znakowi z podanego zakresu. Na przykład „[a-z]” odpowiada dowolnemu małej literze alfabetu od „a” do „z”.

[^m-z] 
Zakres znaków ujemny. Odpowiada dowolnemu znakowi, który nie występuje w podanym ciągu. Na przykład „[^m-z]” odpowiada dowolnemu znakowi, który nie występuje między „m” a „z”.
\b 
Odpowiada granicy reprezentującej słowo, innymi słowy, pozycji między słowem a spacją. Na przykład „er\b” odpowiada „er” w słowie „lever”, ale nie odpowiada „er” w słowie „verbe”.
\B 
Odpowiada granicy, która nie reprezentuje słowa. „en*t\B” odpowiada „ent” w słowie „bien entendu”.
\d 
Odpowiada znakowi reprezentującemu cyfrę. Jest równoważne [0-9].
\D 
Odpowiada znakowi, który nie jest cyfrą. Jest równoważny [^0-9].
\f 
Odpowiada znakowi końca strony.
\n 
Odpowiada znakowi nowego wiersza.
\r 
Odpowiada znakowi powrotu karetki.
\s 
Odpowiada dowolnemu znakowi spacji, w tym spacji, tabulacji, znakowi końca strony itp. Odpowiada „[ \f\n\r\t\v]”.
\S 
Odpowiada dowolnemu znakowi spacji niebędącemu spacją. Odpowiada „[^ \f\n\r\t\v]”.
\t 
Odpowiada znakowi tabulacji.
\v 
Odpowiada znakowi pionowego tabulacji.
\w 
Odpowiada dowolnemu znakowi reprezentującemu słowo i zawierającemu podkreślenie. Odpowiada „[A-Za-z0-9_]”.
\W 
Odpowiada dowolnemu znakowi, który nie reprezentuje słowa. Odpowiada „[^A-Za-z0-9_]”.

\num 
Odpowiada num, gdzie num jest dodatnią liczbą całkowitą. Odnosi się do zapisanych powiązań. Na przykład „(.)\1” odpowiada dwóm identycznym, następującym po sobie znakom.

\n
Odpowiada n, gdzie n jest ośmiokrotną wartością escape. Ośmiokrotne wartości escape muszą składać się z 1, 2 lub 3 cyfr. Na przykład zarówno „\11”, jak i „\011” odpowiadają znakowi tabulacji. „\0011” jest równoważne „\001” i „1”. Oktalowe wartości escape nie mogą przekraczać 256. W przeciwnym razie w wyrażeniu brane pod uwagę będą tylko dwie pierwsze cyfry. Umożliwia używanie kodów ASCII w wyrażeniach regularnych.

\xn
Odpowiada n, gdzie n jest szesnastkową wartością escape. Wartości escape w systemie szesnastkowym muszą obowiązkowo składać się z dwóch cyfr. Na przykład „\x41” odpowiada literze „A”. „\x041” jest równoważne „\x04” i „1”. Umożliwia stosowanie kodów ASCII w wyrażeniach regularnych.

Element w szablonie może występować w jednej lub wielu instancjach. Rozważmy kilka przykładów dotyczących symbolu \d, który reprezentuje jedną cyfrę:

szablon
znaczenie
\d
cyfra
\d?
0 lub 1 cyfra
\d*
0 lub więcej cyfr
\d+
1 lub więcej cyfr
\d{2}
2 cyfry
\d{3,}
co najmniej 3 cyfry
\d{5,7}
od 5 do 7 cyfr

Wyobraźmy sobie teraz model zdolny do opisania oczekiwanego formatu ciągu znaków:

poszukiwany ciąg znaków
model
une date au format jj/mm/aa
\d{2}/\d{2}/\d{2}
une heure au format hh:mm:ss
\d{2}:\d{2}:\d{2}
un nombre entier non signé
\d+
un suite d'espaces éventuellement vide
\s*
un nombre entier non signé qui peut être précédé ou suivi d'espaces
\s*\d+\s*
un nombre entier qui peut être signé et précédé ou suivi d'espaces
\s*[+|-]?\s*\d+\s*
un nombre réel non signé qui peut être précédé ou suivi d'espaces
\s*\d+(.\d*)?\s*
un nombre réel qui peut être signé et précédé ou suivi d'espaces
\s*[+|]?\s*\d+(.\d*)?\s*
une chaîne contenant le mot juste
\bjuste\b
  

Można określić, w którym miejscu ciągu znaków ma być wyszukiwany wzorzec:

wzór
znaczenie
^modèle
szablon rozpoczyna ciąg
modèle$
szablon kończy ciąg
^modèle$
szablon rozpoczyna i kończy ciąg
modèle
wzorzec jest wyszukiwany w całym ciągu, zaczynając od jego początku.
szukany ciąg
wzorzec
une chaîne se terminant par un point d'exclamation
!$
une chaîne se terminant par un point
\.$
une chaîne commençant par la séquence //
^//
une chaîne ne comportant qu'un mot éventuellement suivi ou précédé d'espaces
^\s*\w+\s*$
une chaîne ne comportant deux mot éventuellement suivis ou précédés d'espaces
^\s*\w+\s*\w+\s*$
une chaîne contenant le mot secret
\bsecret\b

Podzbiory wzorca można „wyodrębnić”. Dzięki temu nie tylko można sprawdzić, czy ciąg znaków odpowiada konkretnemu wzorcowi, ale także wyodrębnić z tego ciągu elementy odpowiadające podzbiorom wzorca, które zostały ujęte w nawiasy. Jeśli więc analizujemy ciąg zawierający datę dd/mm/rr i chcemy dodatkowo wyodrębnić elementy dd, mm, rr z tej daty, użyjemy wzorca (\d\d)/(\d\d)/(\d\d).

4.9.1. Sprawdzanie, czy ciąg znaków odpowiada podanemu wzorcowi

Obiekt typu Regex tworzy się w następujący sposób:

Public Sub New(ByVal pattern As String)

Konstruktor tworzy obiekt „wyrażenia regularnego” na podstawie wzorca przekazanego jako parametr (pattern). Po utworzeniu wzorca wyrażenia regularnego można go porównać z ciągami znaków za pomocą metody IsMatch:

Overloads Public Function IsMatch(ByVal input As String) As Boolean

IsMatch zwraca wartość true, jeśli ciąg znaków input odpowiada wzorcowi wyrażenia regularnego. Oto przykład:


' opcje
Option Strict On
Option Explicit On 

' przestrzenie nazw
Imports System
Imports System.Collections
Imports System.Text.RegularExpressions

Module regex1

    Sub Main()
        ' wyrażenie regularne jako wzorzec
        Dim modèle1 As String = "^\s*\d+\s*$"
        Dim regex1 As New Regex(modèle1)
        ' porównanie egzemplarza z wzorcem
        Dim exemplaire1 As String = "  123  "
        If regex1.IsMatch(exemplaire1) Then
            affiche(("[" + exemplaire1 + "] correspond au modèle [" + modèle1 + "]"))
        Else
            affiche(("[" + exemplaire1 + "] ne correspond pas au modèle [" + modèle1 + "]"))
        End If
        Dim exemplaire2 As String = "  123a  "
        If regex1.IsMatch(exemplaire2) Then
            affiche(("[" + exemplaire2 + "] correspond au modèle [" + modèle1 + "]"))
        Else
            affiche(("[" + exemplaire2 + "] ne correspond pas au modèle [" + modèle1 + "]"))
        End If
    End Sub

    ' wyświetla 
    Sub affiche(ByVal msg As String)
        Console.Out.WriteLine(msg)
    End Sub
End Module

oraz wyniki wykonania:

dos>vbc /r:system.dll regex1.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573

dos>regex1
[  123  ] correspond au modèle [^\s*\d+\s*$]
[  123a  ] ne correspond pas au modèle [^\s*\d+\s*$]

4.9.2. Znajdź wszystkie elementy ciągu znaków pasujące do wzorca

Metoda Matches

Overloads Public Function Matches(ByVal input As String) As MatchCollection

zwraca zbiór elementów ciągu znaków input pasujących do wzorca, jak pokazano w poniższym przykładzie:


' opcje
Option Strict On
Option Explicit On 

' przestrzenie nazw
Imports System
Imports System.Collections
Imports System.Text.RegularExpressions

Module regex2
    Sub Main()
        ' wiele wystąpień wzorca w egzemplarzu
        Dim modèle2 As String = "\d+"
        Dim regex2 As New Regex(modèle2)        '
        Dim exemplaire3 As String = "  123  456  789 "
        Dim résultats As MatchCollection = regex2.Matches(exemplaire3)
        affiche(("Modèle=[" + modèle2 + "],exemplaire=[" + exemplaire3 + "]"))
        affiche(("Il y a " & résultats.Count & " occurrences du modèle dans l'exemplaire "))
        Dim i As Integer
        For i = 0 To résultats.Count - 1
            affiche((résultats(i).Value & " en position " & résultats(i).Index))
        Next i
    End Sub

    'wyświetla
    Sub affiche(ByVal msg As String)
        Console.Out.WriteLine(msg)
    End Sub
End Module

Klasa MatchCollection posiada właściwość Count, która określa liczbę elementów w kolekcji. Jeśli résultats jest obiektem MatchCollection, to résultats(i) jest elementem i tej kolekcji i ma typ Match. W naszym przykładzie résultats to zbiór elementów ciągu exemplaire3 odpowiadających modelowi modèle2, a résultats(i) jest jednym z tych elementów. Klasa Match posiada dwie właściwości, które nas tutaj interesują:

  • Value: wartość obiektu Match, czyli elementu odpowiadającego modelowi
  • Index: pozycja, na której element został znaleziony w przeszukiwanym ciągu

Wyniki wykonania poprzedniego programu:

dos>vbc /r:system.dll regex2.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4p our Microsoft (R) .NET Framework version 1.1.4322.573

dos>regex2
Modèle=[\d+],exemplaire=[  123  456  789 ]
Il y a 3 occurrences du modèle dans l'exemplaire
123 en position 2
456 en position 7
789 en position 12

4.9.3. Pobieranie części wzorca

Podzbiory wzorca można „wyodrębnić”. Dzięki temu nie tylko można sprawdzić, czy ciąg znaków odpowiada konkretnemu wzorcowi, ale także wyodrębnić z tego ciągu elementy odpowiadające podzbiorom wzorca, które zostały ujęte w nawiasy. Jeśli więc analizujemy ciąg znaków zawierający datę dd/mm/rr i chcemy dodatkowo wyodrębnić elementy dd, mm, rr z tej daty, użyjemy wzorca (\d\d)/(\d\d)/(\d\d). Rozważmy następujący przykład:


' opcje
Option Strict On
Option Explicit On 

' przestrzenie nazw
Imports System
Imports System.Collections
Imports System.Text.RegularExpressions

Module regex2
    Sub Main()
        ' pobieranie elementów z szablonu
        Dim modèle3 As String = "(\d\d):(\d\d):(\d\d)"
        Dim regex3 As New Regex(modèle3)
        Dim exemplaire4 As String = "Il est 18:05:49"
        ' weryfikacja szablonu
        Dim résultat As Match = regex3.Match(exemplaire4)
        If résultat.Success Then
            ' egzemplarz jest zgodny z szablonem
            affiche(("L'exemplaire [" + exemplaire4 + "] correspond au modèle [" + modèle3 + "]"))
            ' wyświetlanie grup
            Dim i As Integer
            For i = 0 To résultat.Groups.Count - 1
                affiche(("groupes[" & i & "]=[" & résultat.Groups(i).Value & "] en position " & résultat.Groups(i).Index))
            Next i
        Else
            ' egzemplarz nie odpowiada szablonowi
            affiche(("L'exemplaire[" + exemplaire4 + " ne correspond pas au modèle [" + modèle3 + "]"))
        End If
    End Sub

    'wyświetla
    Sub affiche(ByVal msg As String)
        Console.Out.WriteLine(msg)
    End Sub
End Module

Wykonanie tego programu daje następujące wyniki:

dos>vbc /r:system.dll regex3.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4

dos>regex3
L'exemplaire [Il est 18:05:49] correspond au modèle [(\d\d):(\d\d):(\d\d)]
groupes[0]=[18:05:49] en position 7
groupes[1]=[18] en position 7
groupes[2]=[05] en position 10
groupes[3]=[49] en position 13

Nowość znajduje się w następującym fragmencie kodu:


        ' weryfikacja szablonu
        Dim résultat As Match = regex3.Match(exemplaire4)
        If résultat.Success Then
            ' egzemplarz odpowiada szablonowi
            affiche(("L'exemplaire [" + exemplaire4 + "] correspond au modèle [" + modèle3 + "]"))
            ' wyświetlane są grupy
            Dim i As Integer
            For i = 0 To résultat.Groups.Count - 1
                affiche(("groupes[" & i & "]=[" & résultat.Groups(i).Value & "] en position " & résultat.Groups(i).Index))
            Next i
        Else

Ciąg znaków exemplaire4 jest porównywany z wzorcem regex3 za pomocą metody Match. Metoda ta zwraca obiekt Match, który został już przedstawiony. Wykorzystujemy tutaj dwie nowe właściwości tej klasy:

  • Success: wskazuje, czy znaleziono dopasowanie
  • Groups: zbiór, w którym
    • Groups[0] odpowiada części ciągu znaków odpowiadającej wzorcowi
    • Groups[i] (i>=1) odpowiada grupie nawiasów o numerze i

Jeśli wynik jest typu Match, résultats.Groups jest typu GroupCollection, a résultats.Groups[i] jest typu Group. Klasa Group posiada dwie właściwości, z których tutaj korzystamy:

  • Value: wartość obiektu Group, której element odpowiada zawartości nawiasu
  • Index: pozycja, w której element został znaleziony w analizowanym ciągu

4.9.4. Program do nauki

Znalezienie wyrażenia regularnego, które pozwoli nam sprawdzić, czy dany ciąg znaków odpowiada określonemu wzorcowi, bywa prawdziwym wyzwaniem. Poniższy program pozwala poćwiczyć tę umiejętność. Wymaga on podania wzorca i ciągu znaków, a następnie wskazuje, czy ciąg ten odpowiada wzorcowi, czy nie.


' opcje
Option Strict On
Option Explicit On 

' przestrzenie nazw
Imports System
Imports System.Collections
Imports System.Text.RegularExpressions
Imports Microsoft.visualbasic

Module regex4
    Sub Main()
        ' wyrażenie regularne wzorca
        Dim modèle, chaine As String
        Dim regex As Regex = Nothing
        Dim résultats As MatchCollection
        ' użytkownik jest proszony o podanie wzorców i egzemplarzy, które mają zostać porównane z tym egzemplarzem
        Dim terminé1 As Boolean = False
        Do While Not terminé1
            Dim erreur As Boolean = True
            Do While Not terminé1 And erreur
                ' pyta się o wzorzec
                Console.Out.Write("Tapez le modèle à tester ou fin pour arrêter :")
                modèle = Console.In.ReadLine()
                ' koniec?
                If modèle.Trim().ToLower() = "fin" Then
                    terminé1 = True
                Else
                    ' tworzy się wyrażenie regularne
                    Try
                        regex = New Regex(modèle)
                        erreur = False
                    Catch ex As Exception
                        Console.Error.WriteLine(("Erreur : " + ex.Message))
                    End Try
                End If
            Loop
            ' gotowe?
            If terminé1 Then Exit Sub
            ' pyta się użytkownika o egzemplarze, które mają zostać porównane z wzorcem
            Dim terminé2 As Boolean = False
            Do While Not terminé2
                Console.Out.Write(("Tapez la chaîne à comparer au modèle [" + modèle + "] ou fin pour arrêter :"))
                chaine = Console.In.ReadLine()
                ' zakończone?
                If chaine.Trim().ToLower() = "fin" Then
                    terminé2 = True
                Else
                    ' przeprowadzamy porównanie
                    résultats = regex.Matches(chaine)
                    ' powodzenie?
                    If résultats.Count = 0 Then
                        Console.Out.WriteLine("Je n'ai pas trouvé de correspondances")
                    Else
                        ' wyświetlane są elementy odpowiadające wzorcowi
                        Dim i As Integer
                        For i = 0 To résultats.Count - 1
                            Console.Out.WriteLine(("J'ai trouvé la correspondance [" & résultats(i).Value & "] en position " & résultats(i).Index))
                            ' podelementy
                            If résultats(i).Groups.Count <> 1 Then
                                Dim j As Integer
                                For j = 1 To (résultats(i).Groups.Count) - 1
                                    Console.Out.WriteLine((ControlChars.Tab & "sous-élément [" & résultats(i).Groups(j).Value & "] en position " & résultats(i).Groups(j).Index))
                                Next j
                            End If
                        Next i
                    End If
                End If
            Loop
        Loop
    End Sub

    'wyświetla
    Sub affiche(ByVal msg As String)
        Console.Out.WriteLine(msg)
    End Sub
End Module

Oto przykładowe wyniki działania:

Tapez le modèle à tester ou fin pour arrêter :\d+
Tapez la chaîne à comparer au modèle [\d+] ou fin pour arrêter :123 456 789
J'ai trouvé la correspondance [123] en position 0
J'ai trouvé la correspondance [456] en position 4
J'ai trouvé la correspondance [789] en position 8
Tapez la chaîne à comparer au modèle [\d+] ou fin pour arrêter :fin

Tapez le modèle à tester ou fin pour arrêter :(\d\d):(\d\d)
Tapez la chaîne à comparer au modèle [(\d\d):(\d\d)] ou fin pour arrêter :14:15 abcd 17:18 xyzt
J'ai trouvé la correspondance [14:15] en position 0
        sous-élément [14] en position 0
        sous-élément [15] en position 3
J'ai trouvé la correspondance [17:18] en position 11
        sous-élément [17] en position 11
        sous-élément [18] en position 14
Tapez la chaîne à comparer au modèle [(\d\d):(\d\d)] ou fin pour arrêter :fin

Tapez le modèle à tester ou fin pour arrêter :^\s*\d+\s*$
Tapez la chaîne à comparer au modèle [^\s*\d+\s*$] ou fin pour arrêter :  1456
J'ai trouvé la correspondance [  1456] en position 0
Tapez la chaîne à comparer au modèle [^\s*\d+\s*$] ou fin pour arrêter :fin

Tapez le modèle à tester ou fin pour arrêter :^\s*(\d+)\s*$
Tapez la chaîne à comparer au modèle [^\s*(\d+)\s*$] ou fin pour arrêter :1456
J'ai trouvé la correspondance [1456] en position 0
        sous-élément [1456] en position 0
Tapez la chaîne à comparer au modèle [^\s*(\d+)\s*$] ou fin pour arrêter :abcd 1
456
Je n'ai pas trouvé de correspondances
Tapez la chaîne à comparer au modèle [^\s*(\d+)\s*$] ou fin pour arrêter :fin

Tapez le modèle à tester ou fin pour arrêter :fin

4.9.5. Metoda Split

Metodę tę poznaliśmy już w klasie String:

Overloads Public Function Split(ByVal ParamArray separator() As Char) As String()

Ciąg znaków jest traktowany jako sekwencja pól rozdzielonych znakami zawartymi w tablicy [separator]. Wynikiem jest tablica tych pól.

Separatorem pól w ciągu znaków jest tutaj jeden ze znaków z tablicy separator. Metoda Split z klasy Regex pozwala nam określić separator na podstawie wzorca:

Overloads Public Function Split(ByVal input As String) As String()

Ciąg znaków [input] jest rozkładany na pola, które są oddzielone separatorem odpowiadającym wzorcowi bieżącego obiektu Regex. Załóżmy na przykład, że w pliku tekstowym mamy wiersze w postaci pole1, pole2, …, polen. Pola są oddzielone przecinkiem, ale przed nim lub po nim mogą występować spacje. Metoda Split klasy string nie jest w tym przypadku odpowiednia. Rozwiązaniem jest metoda RegEx. Jeśli ligne jest odczytanym wierszem, pola można uzyskać za pomocą

dim champs() as string=new Regex("s*,\s*").Split(ligne)

jak pokazuje poniższy przykład:


' opcje
Option Strict On
Option Explicit On 

' przestrzenie nazw
Imports System
Imports System.Text.RegularExpressions

Module regex5
    Sub Main()
        ' wiersz
        Dim ligne As String = "abc  , def  , ghi"
        ' szablon
        Dim modèle As New Regex("\s*,\s*")
        ' podział wiersza na pola
        Dim champs As String() = modèle.Split(ligne)
        ' wyświetlanie
        Dim i As Integer
        For i = 0 To champs.Length - 1
            Console.Out.WriteLine(("champs[" & i & "]=[" & champs(i) & "]"))
        Next i
    End Sub
End Module

Wyniki wykonania:

dos>vbc /r:system.dll regex5.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4

dos>regex5
champs[0]=[abc]
champs[1]=[def]
champs[2]=[ghi]

4.10. Klasy BinaryReader i BinaryWriter

Klasy BinaryReader i BinaryWriter służą do odczytu i zapisu plików binarnych. Rozważmy następującą aplikację. Chcemy napisać program, który wywoływałby się w następujący sposób:

// składnia pg tekst bin
// odczyt pliku tekstowego (tekst) i zapisanie jego zawartości w
// plik binarny
// plik tekstowy zawiera wiersze w postaci: imię : wiek
// które zostaną zapisane w strukturze typu string, int

Plik tekstowy zawiera następującą treść:

paul : 10
helene : 15
jacques : 11
sylvain : 12

Program wygląda następująco:


' opcje
Option Strict On
Option Explicit On 

' przestrzenie nazw
Imports System
Imports System.Text.RegularExpressions
Imports System.IO

' BinaryWriter()

Module bw1
    ' składnia pg tekst bin
    ' odczytuje się plik tekstowy (tekst) i zapisuje jego zawartość w
    ' plik binarny
    ' plik tekstowy zawiera wiersze w postaci: imię : wiek
    ' które zostaną zapisane w strukturze typu string, int

    Sub Main(ByVal arguments() As String)
        ' potrzebne są 2 argumenty
        Dim nbArgs As Integer = arguments.Length
        If nbArgs <> 2 Then
            Console.Error.WriteLine("syntaxe : pg texte binaire")
            Environment.Exit(1)
        End If
        ' otwarcie pliku tekstowego do odczytu
        Dim input As StreamReader = Nothing
        Try
            input = New StreamReader(arguments(0))
        Catch ex As Exception
            Console.Error.WriteLine("Erreur d'ouverture du fichier " & arguments(0) & "(" & ex.Message & ")")
            Environment.Exit(2)
        End Try
        ' otwarcie pliku binarnego do zapisu
        Dim output As BinaryWriter = Nothing
        Try
            output = New BinaryWriter(New FileStream(arguments(1), FileMode.Create, FileAccess.Write))
        Catch ex As Exception
            Console.Error.WriteLine("Erreur d'ouverture du fichier " & arguments(1) & "(" & ex.Message & ")")
            Environment.Exit(3)
        End Try
        ' odczyt pliku tekstowego – zapis pliku binarnego
        ' wiersz pliku tekstowego
        Dim ligne As String
        ' separator pól w wierszu
        Dim séparateur As New Regex("\s*:\s*")
        ' wzór wieku
        Dim modAge As New Regex("\s*\d+\s*")
        Dim numLigne As Integer = 0
        Dim traitementFini As Boolean
        Dim champs As String()
        ligne = input.ReadLine()
        While Not (ligne Is Nothing)
            traitementFini = False
            ' pusty wiersz?
            If ligne.Trim() = "" Then
                traitementFini = True
            End If
            ' kolejny wiersz
            If Not traitementFini Then
                numLigne += 1
                ' wiersz: imię i nazwisko: wiek
                champs = séparateur.Split(ligne)
                ' potrzebujemy 2 pól
                If champs.Length <> 2 Then
                    Console.Error.WriteLine(("La ligne n° " & numLigne & " du fichier " & arguments(0) & " a un nombre de champs incorrect"))
                    ' następny wiersz
                    traitementFini = True
                End If
            End If
            If Not traitementFini Then
                ' drugie pole musi być liczbą całkowitą >=0
                If Not modAge.IsMatch(champs(1)) Then
                    Console.Error.WriteLine(("La ligne n° " & numLigne & " du fichier " & arguments(0) & " a un âge incorrect"))
                    ' kolejny wiersz
                    traitementFini = True
                End If
                ' zapisujemy dane do pliku binarnego
                output.Write(champs(0))
                output.Write(Integer.Parse(champs(1)))
            End If
            'następny wiersz
            ligne = input.ReadLine()
        End While
        ' zamknięcie plików
        input.Close()
        output.Close()
    End Sub
End Module

Przyjrzyjmy się operacjom dotyczącym klasy BinaryWriter:

  • obiekt BinaryWriter jest otwierany przez operację

            output = New BinaryWriter(New FileStream(arguments(1), FileMode.Create, FileAccess.Write))

Argumentem konstruktora musi być strumień (Stream). W tym przypadku jest to strumień utworzony na podstawie pliku (FileStream), dla którego podajemy:

  • (ciąg dalszy)
    • nazwę
    • operację do wykonania, w tym przypadku FileMode.Create w celu utworzenia pliku
    • typ dostępu, w tym przypadku FileAccess.Write dla dostępu z prawem zapisu do pliku
  • operację zapisu

                 ' zapis danych do pliku binarnego
                output.Write(champs(0))
                output.Write(Integer.Parse(champs(1)))

Klasa BinaryWriter posiada różne nadpisane metody Write służące do zapisywania różnych typów danych prostych

  • operacja zamknięcia strumienia
        output.Close()

Wyniki poprzedniego wykonania zostaną nam podane przez poniższy program. Program ten również przyjmuje dwa argumenty:

     ' składnia pg bin tekst
     ' odczyt pliku binarnego bin i zapisanie jego zawartości w pliku tekstowym (tekst)
     ' plik binarny ma strukturę typu string, int
     ' plik tekstowy zawiera wiersze w postaci: imię : wiek

Wykonujemy więc operację odwrotną. Odczytujemy plik binarny, aby utworzyć plik tekstowy. Jeśli wygenerowany plik tekstowy będzie identyczny z plikiem źródłowym, będziemy mieli pewność, że konwersja tekst → binarny → tekst przebiegła pomyślnie. Kod wygląda następująco:


' opcje
Option Strict On
Option Explicit On 

' przestrzenie nazw
Imports System
Imports System.Text.RegularExpressions
Imports System.IO

Module br1
    ' składnia pg bin tekst
    ' odczytuje się plik binarny bin i zapisuje jego zawartość w pliku tekstowym (tekst)
    ' plik binarny ma strukturę typu string, int
    ' plik tekstowy zawiera wiersze w postaci: imię : wiek

    Sub Main(ByVal arguments() As String)
        ' potrzebne są 2 argumenty
        Dim nbArgs As Integer = arguments.Length
        If nbArgs <> 2 Then
            Console.Error.WriteLine("syntaxe : pg binaire texte")
            Environment.Exit(1)
        End If
        ' otwarcie pliku binarnego do odczytu
        Dim dataIn As BinaryReader = Nothing
        Try
            dataIn = New BinaryReader(New FileStream(arguments(0), FileMode.Open, FileAccess.Read))
        Catch ex As Exception
            Console.Error.WriteLine("Erreur d'ouverture du fichier " & arguments(0) & "(" & ex.Message & ")")
            Environment.Exit(2)
        End Try
        ' otwarcie pliku tekstowego do zapisu
        Dim dataOut As StreamWriter = Nothing
        Try
            dataOut = New StreamWriter(arguments(1))
            dataOut.AutoFlush = True
        Catch ex As Exception
            Console.Error.WriteLine("Erreur d'ouverture du fichier " & arguments(1) & "(" & ex.Message & ")")
            Environment.Exit(3)
        End Try
        ' odczyt pliku binarnego – zapis pliku tekstowego
        Dim nom As String        ' nom d'une personne
        Dim age As Integer        ' son âge
        ' pętla przetwarzania pliku binarnego
        While True
            ' odczyt nazwy
            Try
                nom = dataIn.ReadString()
            Catch
                ' koniec pliku
                Exit Sub
            End Try
            ' odczyt wieku
            Try
                age = dataIn.ReadInt32()
            Catch
                Console.Error.WriteLine("Le fichier " & arguments(0) + " ne semble pas avoir un format correct")
                Exit Sub
            End Try
            ' zapis do pliku tekstowego
            dataOut.WriteLine(nom & ":" & age)
            System.Console.WriteLine(nom & ":" & age)
        End While
        ' zamykamy wszystko
        dataIn.Close()
        dataOut.Close()
    End Sub
End Module

Przyjrzyjmy się operacjom dotyczącym klasy BinaryReader:

  • obiekt BinaryReader jest otwierany przez operację

            dataIn = New BinaryReader(New FileStream(arguments(0), FileMode.Open, FileAccess.Read))

Argumentem konstruktora musi być strumień (Stream). W tym przypadku jest to strumień utworzony na podstawie pliku (FileStream), dla którego podajemy:

  • (ciąg dalszy)
    • nazwę
    • operację do wykonania, w tym przypadku FileMode.Open w celu otwarcia istniejącego pliku
    • typ dostępu, w tym przypadku FileAccess.Read dla dostępu do pliku w trybie odczytu
  • operację odczytu
                nom = dataIn.ReadString()
                age = dataIn.ReadInt32()

Klasa BinaryReader posiada różne metody ReadXX do odczytu różnych typów danych prostych

  • operacja zamknięcia strumienia
        dataIn.Close()

Jeśli uruchomimy oba programy po kolei, przekształcając personnes.txt w personnes.bin, a następnie personnes.bin w personnes.txt2, otrzymamy:

dos>more personnes.txt
paul : 10
helene : 15
jacques : 11
sylvain : 12

dos>more personnes.txt2
paul:10
helene:15
jacques:11
sylvain:12

dos>dir
29/04/2002  18:19                   54 personnes.txt
29/04/2002  18:19                   44 personnes.bin
29/04/2002  18:20                   44 personnes.txt2