Programujeme ve Visual Basic .NET - 23. díl - textové řetězce

Tento díl seriálu o VB.NET je věnován základům práce s textem - deklaraci a inicializaci textových znaků a řetězců.

Datový typ řetězec

Popis práce s textovými  řetězci tvoří v určitém smyslu předěl úvodní části seriálu, věnované klasickým metodám programování. S řetězci se totiž můžeme setkat v programech od prvních verzí BASIC-u. Dokonce i úvodní ukázka typu "HelloWorld" v prvním dílu seriálu je vlastně elementární demonstrace práce s textovým řetězcem:

Module Module2
  Sub Main()
    Console.Write("Hello, world")
  End Sub
End Module

Z uvedeného vyplývá, že řetězce tvoří jeden ze základních a nejdůležitějších datových typů vůbec. Pojem textový řetězec (anglicky "string") bychom neměli zaměňovat s pojmem literál což je alfanumerický text (text složený z kombinace písmen a číslic), sloužící k přiřazení hodnoty proměnné nebo konstanty. Zatímco číselný literál slouží k přiřazení číselné konstanty do hodnoty proměnné, textový literál je řetězcová konstanta, uzavíraná do dvojitých uvozovek (ASCII znak s kódem 24: Chr(24)). V tomto smyslu  je výstupem programu  "HelloWorld" text, vzniklý voláním metody Write() s textovým literálem jako parametrem. Vlastní textový řetězec pak představuje našemu pohledu skrytý blok bajtů v paměti, který je metodou Write() zpracován na textový výstup do příkazového řádku.

V případě, že v literálu potřebujeme vypsat samotnou dvojitou uvozovku, musíme ji zdvojit. Syntaxe VB.NET na rozdíl od C-jazyků (C, C++, C# nebo Java, JavaScript) nepodporuje tzv. escaping, což je možnost vypsání speciálních a řídících znaků (vč. uvozovek) pomocí sekvence jiných znaků a proto jsou všechny řetězce VB.NET doslovné (anglicky verbatime) a při práci s nimi je nutné sledovat párování uvozovek, např. ukázka níže:

System.Console.Write("Toto je ""uvozený text""")

do příkazového řádku vypíše následující text:

Toto je "uvozený text"

Deklarace a inicializace řetězce

Vestavěnému datovému typu VB.NET String odpovídá datový typ CTS System.String. Proměnné typu řetězec se někdy podle tzv. maďarské notace označují předponou "s-" nebo "str-" ("string"). Namísto označení typu String lze též použít příponu - specifikátor typu řetězec, který už od ranných verzí BASIC-u tvoří znak dolar ("$"). Nevýhodou úsporného zápisu s použitím specifikátoru typu je jeho snížená čitelnost. K inicializaci objektu typu řetězec lze krom toho využít i přetížený konstruktor (inicializační proceduru) třídy System.String, vyžadující uzavření svého parametru do závorek - níže uvedené příklady inicializace řetězce jsou tedy funkčně shodné:

Dim sText$ = "Toto je můj řetězec"
Dim sText As String = "Toto je můj řetězec"
Dim sText As System.String = "Toto je můj řetězec"
Dim sText As New System.String("Toto je můj řetězec")

Konstruktor třídy System.String umožňuje výhodně inicializovat řetězce i dalšími způsoby, které lze využít, pokud potřebujeme inicializovat řetězec určité délky a/nebo vyplněný určitými znaky:

Dim sTabs As New String(System.Convert.ToChar(9), 5)	` vytvoří řetězec složený z pěti znaků tabulátoru: Chr(9)
Dim sSpaces As New String(" ", 20)			` vytvoří řetězec tvořený dvaceti mezerami

Poznámka: V předchozích verzích VB podobnému cíli sloužila funkce String(), která byla ve VB.NET nahrazena funkcí StrDup() modulu Microsoft.VisualBasic.Strings,  popř. Space(), vracející řetězec tvořený daným počtem mezer. Tyto metody by v nových programech již neměly být používány, podobně jako ostatní metody modulu Microsoft.VisualBasic.Strings a uvádíme je zde pouze pro úplnost, neboť se mohou vyskytovat ve zdrojových kódech konvertovaných ze starších verzí VB.

Řetězce jako pole znaků

Řetězce jsou stejně jako pole referenčním datovým typem, důvodem je fakt, že jsou dynamické a mohou nabývat neomezené délky, limitované pouze velikostí paměťové haldy - množstvím dostupné paměti (spravované .NET CLR)  Důvodem, proč jsou v tomto seriálu  řetězce probírány po výkladu polí je ten, že pole jsou vnitřně reprezentovány jednorozměrným polem znaků a lze je proto převést na pole znaků metodou ToCharArray(), např.   

Dim myString As System.String = "Toto je můj řetězec"
Dim myArray As System.Char() = myString.ToCharArray
Console.Write("Toto je můj řetězec".ToCharArray().GetUpperBound(0)))

a aplikovat metody pro práci s polem - např. délku řetězce (počet znaků v řetězci) nám vypíše vestavěná vlastnost Length:

Console.Write(myString.Length)
Console.Write("Toto je text".Length)

Pole znaků lze naopak převést zpátky na řetězec předáním konstruktoru nové instance třídy System.String, takže například následující ukázka

Dim sText As String = "Toto je můj řetězec"
Dim aChar As Char() = sText.ToCharArray
Array.Sort(aChar)
Console.Write(New String(aChar))

vypíše řetězec znaků (včetně původně obsažených mezer) seřazených podle kódu znaků výchozí znakové sady Unicode:

Tceeejjmoottzěřů

Pokud potřebujeme získat pouze odkaz na jednotlivé prvky pole znaků, které odpovídají danému řetězci, postačí použít vestavěnou vlastnost Chars() třídy System.String, která přebírá index znaku, počítaný od nuly. Tímto způsobem můžeme procházet jednotlivé znaky v řetězci pomocí indexu:

Dim myChar As System.Char = myString.Chars(3)
Console.Write("Toto je můj řetězec".Char(6))

Předchozí ukázka vypíše do příkazové řádky šestý znak v řetězci, číslováno od nuly, tedy písmeno "e".

Poznámky:

  • K získání více znaků, tj. části řetězce uvnitř jiného řetězce slouží metoda Substring() třídy System.String, popř. lze využít starší metody Left(), Right() a Mid() modulu Microsoft.VisualBasic.Strings se kterými se seznámíme později.
  • Pokud potřebujeme konvertovat řetězec na pole bajtů místo na pole znaků, nebo přistupovat ke znakům v řetězci v jiném, než výchozím kódování nebo řadit řetězce podle národní sady znaků můžeme využít třídy jmenného prostoru System.Text pro práci se znakovými sadami, kterým bude věnován samostatný díl seriálui.

Práce s řetězci v paměti

Stejně jako pole není řetězec datovým typem s pevnou délkou. Řetězec v paměti zabírá 20+n x 2 bajty, zaokrouhleno na n/2 bajtů směrem dolů), kde n je délka řetězce stanovená metodou Length(). Každé dva bajty představují  jeden Unicode znak (anglicky character), jehož hodnota může měnit v rozmezí 0 - 216 bajtů) a aktuální konec řetězce je v paměti označen znakem, odpovídajícím nulovému bajtu (tzv. nulový znak). V prostředí VB.NET je nulovému znaku vyhražena pojmenovaná konstanta vbNullChar.. Naproti tomu pojem prázdný řetězec, pro který je v prostředí VB.NET vyhražena konstanta vbNullString, odpovídá poli znaků nulové délky a inicializujeme jej pomocí dvojice uvozovek, nebo pole Empty třídy System.String, která nabývá tutéž hodnotu:

Dim sNullChar As Char = vbNullChar
Dim sNullChar As Char = System.Convert.ToChar(0)
Dim sNull As String = Microsoft.VisualBasic.Constants.vbNullString

Dim sEmpty As String = ""
Dim sEmpty As String = String.Empty

Řetězce VB.NET jsou neměnný (immutable) referenční typ, což znamená, že není možné jednoduše (bez použití unsafe pointeru) změnit část řetězce za jiný např. operací přiřazení, vždy je v paměti vytvořena další  instance třídy SystemString, do níž je přiřazena nová hodnota:.

Dim sText As String = "Toto je řetězec"
sText = "Toto je proměnná s novým řetězcem"

Důsledkem uvedeného faktu při práci s delšími řetězci je, že se v paměti při každém přířazení do řetězcové proměnné vyhradí (alokuje) celý blok paměti znovu - a to i tehdy, když se rozhodneme připojit k dosavadnímu řetězci třeba jen jediný znak nebo řetězec předáváme proceduře odkazem - což činí opakované operace s delšími řetězci značně neefektivní. Prostředí .NET tento problém kompenzuje dvěma způsoby.  Jednak na pozadí udržuje pro řetězce speciální paměťový fond (tzv. pool), který představuje pro řetězce alokační cache. Je-li potřeba vytvořit již jednou v minulosti použitý řetězec, aplikace místo nového řetězce dostane odkaz na prvně alokovaný řetězec, aniž o to musí programátor jakkoliv pečovat. Druhé řešení je použití třídy System.Text.StringBuilder, které výrazně urychluje manipulace s delšími řetězci tím, že pro ně alokuje izolovaný blok paměti (tzv. buffer) a jejímu použití věnujeme samostatný díl seriálu.

Poznámka: Konstanta vbNullString rázdný řetězec ("blank string")  známá z předchozích verzí VB/VBA v prostředí VB.NET odpovídá hodnotě Nothing a hodnotou odpovídá řetězi nulové délky.

Datový typ znak

Jak již bylo uvedeno výše, každý řetězec formálně představuje pole znaků a lze je na toto pole bez ztráty informace převést. Datový typ znak je na rozdíl od řetězce hodnotový datový typ, představující dvoubajtový Unicode znak zapouzdřený strukturou System.Char bez konstruktoru. Interně je reprezentován dvoubajtovým číslem bez znaménka v rozsahu (0 - 216) , jehož numerickou hodnotu vypíše převodní metoda ToInt16() třídy  System.Convert. Obráceně, datový typ znak můžeme inicializovat ve výše uvedeném rozsahu jeho hodnotou pomocí metody System.Convert.ToChar(). Ukázka deklaruje a inicializuje netisknutelný znak č. 9 (známý jako řídící znak tabelátor) a vypíše zpátky jeho numerickou hodnotu.

Dim ch As System.Char = System.Convert.ToChar(9)    
System.Console.WriteLine(System.Convert.ToInt16(ch))

Tisknutelné znaky můžeme pohodlněji inicializovat literálem s využitím specifikátoru typu, který pro znak představuje písmeno "c". Specifikátor typu má význam např. při operaci porovnání metodou System.Char.Equals(), která rozlišuje mezi datovým typem znak a řetězec. K převodu řetězce na znak můžeme také použít metodu Char.Parse(), jejíž výstup se řídí prvním nalezeným znakem řetězce zleva:

Dim chA As Char = "A"c, chA as Char = System.Char.Parse("Abeceda")

Console.WriteLine(chA.Equals("A"))          	` výstup: "False", "A" je řetězec
Console.WriteLine(chA.Equals("A"c))         	` výstup: "True"
Console.WriteLine(chA.Equals(chB))         	` výstup: "True"

Třída System.Char disponuje tabulkou metod, které umožňují každý řetězec začlenit do jedné či více výčtových skupin typu System.Globalization.UnicodeCategory:

Dim chA As Char = "A"c, ch1 As Char  = "1"c, ch_ = " "c

Console.WriteLine(chA.CompareTo("B"c))  	` výstup: "-1" znamená "A"c má kód o jedničku nižší, než "B"c
Console.WriteLine(Char.GetNumericValue(ch1))    ` výstup: "1"
Console.WriteLine(Char.ToLower(chA))   		` výstup: "a"
Console.WriteLine(Char.IsControl(ch_))       	` výstup: "False", mezera není řídící znak
Console.WriteLine(Char.IsDigit(ch1))    	` výstup: "True",  znak "1"c je číslicí
Console.WriteLine(Char.IsLetter(ch_))  		` výstup: "False", znak " "c není písmeno
Console.WriteLine(Char.IsLetterOrDigit(chA))  	` výstup: "True",  znak "A"c je alfanumerický znak
Console.WriteLine(Char.IsLower(chA))   		` výstup: "False", znak "A"c není znak velké abecedy
Console.WriteLine(Char.IsNumber(ch1))   	` výstup: "True",  znak "1" je číslem
Console.WriteLine(Char.IsPunctuation(ch_))      ` výstup: "True",  znak " "c není znak interpunkce.
Console.WriteLine(Char.IsSeparator(ch_))     	` výstup: "True",  znak " "c je oddělovač
Console.WriteLine(Char.IsSymbol(ch1))  		` výstup: "False", znak "1"c není symbol
Console.WriteLine(Char.IsWhiteSpace(ch_))    	` výstup: "True",  znak " "c je tisknutelná mezera

Poznámka: K inicializaci 16-bitového Unicode znaku je možné použít funkci Chr(), resp. ChrW() modulu Microsoft.VisualBasic.Strings, k inicializaci 8-bitového znaku funkci ChrB(). S oběma funkcemi se seznamíme později při popisu funkck pro práci s řetězci a znaky VB.NET.

Řídící znaky systému Windows

Běžně používané znaky a kombinace mají přiřazeny pojmenované konstanty, které bychom měli používat v kódu místo explicitních hodnot. Platí to především o znaku oddělovače řádků, který může nabývat v závislosti na systémovém nastavení různých hodnot (na Unix systémech je to hodnota vbLf, na Windows systémech hodnota vbCrLf. Systémově proměnné konstanty získáme jako statická pole třídy System.Environment. S ohledem na kompatibilitu se staršími verzemi VB jsou podporovány i pojmenované konstanty modulu Microsoft.VisualBasic.ControlChars jejichž názvy začínají  předponou "vb-", ale v nových aplikacích bychom měli dát přednost konstantám třídy System.Environment:

Pojmenované znakové konstanty VB.NET
System.Environment   Microsoft.VisualBasic   Chr(13) + Chr(10) popis a použití znaku
CrLf vbCrLf Chr(13) + Chr(10) kombinace znaku nového řádku a návratu na začátek řádku
Cr vbCr Chr(13) znak nového řádku ("Carriage return")
Lf vbLf Chr(10) znak návratu na začátek řádku ("linefeed"), oddělovač řádku v Unix systémech  
NewLine vbNewLine Chr(13) + Chr(10) kombinace znaku nového řádku a návratu na začátek řádku
NullChar vbNullChar Chr(0) znak s hodnotou 0, "nulový znak"
 - vbNullString  blank string řetězec s hodnotou 0, "nulový řetězec"
Tab vbTab Chr(9) znak tabelátoru
Back vbBack Chr(8) znak návrat zpět (Backspace)
Quote  - Chr(34) znak uvozovky

Následující deklarace znaku chEnter jsou vzájemně ekvivalentní. Všechny inicializují kombinaci řetězců, která se v systému Windows používá jako oddělovač řádku:

Dim chEnter As System.Char = System.Environment.NewLine
Dim chEnter As System.Char = Microsoft.VisualBasic.Constants.vbCrLf
Dim chEnter As System.Char = Microsoft.VisualBasic.ControlChars.CrLf
Dim chEnter As System.Char = Microsoft.VisualBasic.ControlChars.NewLine
Dim chEnter As System.Char = System.Convert.ToChar(13) + System.Convert.ToChar(10)
Dim chEnter As System.Char = Microsoft.VisualBasic.Strings.Chr(13) + Microsoft.VisualBasic.Strings.Chr(10)

Stručné shrnutí

Na několika příkladech jsme se seznámili s nejběžnějšími způsoby deklarace a inicializace znaků a textových řetězců. Řetězce VB.NET představují důležitý referenční datový typ, který lze interpretovat jako dynamicky deklarované pole Unicode znaků obsazujících v paměti po dvou bajtech. Nejdůležitější znaky používané jako řídíci znaky mají v prostředí VB.NET přiřazeny pojmenované konstanty.

V příštím dílu se seznámíme s nejběžnějšími operacemi s řetězci prostřednictvím metod třídy System.String a modulu Microsoft.VisualBasic.Strings.

Diskuze (11) Další článek: Ke stažení: Nový Total Commander a PDFCreator zdarma

Témata článku: , , , , , , , , , , , , , , , , , , , , , , , , ,