Práce s textem a řetězci

12. díl - Práce s textem a řetězci

Tomáš Herceg       17.08.2007       VB.NET, Algoritmy, .NET       60786 zobrazení

V tomto díle se podíváme velmi podrobně na práci s textem, vyhledávání, rozdělování a spojování, převody na velká a malá písmena, práci s částmi textu atd. Ukážeme si také, jak používat konce řádků a spoustu dalších věcí.

V minulém dílu jsem slíbil, že se budeme věnovat práci s textem a textovými řetězci. Nejprve si ukážeme nejpoužívanější vlastnosti a metody datového typu String. Pro hodnotu typu String budu používat termín text či řetězec, pro část této hodnoty budu používat termín podřetězec či sekvence znaků.

Zjištění délky řetězce

Každý řetězec má vlastnost Length, která vrátí jeho délku, čili celkový počet znaků.

        'zjištění délky textu  
        Dim txt As String = "Červená Karkulka potkala vlka."
        MsgBox(txt.Length)                'vypíše 30

Práce s jednotlivými znaky

Pokud potřebujeme získat například šesté písmeno z textu, použijeme vlastnost Chars, což je pole znaků. Znaky se vždycky číslují od nuly, i ve všech dalších ukázkách.

        'vypsání jednoho znaku z textu
        Dim txt4 As String = "abcdefgh!"
        MsgBox(txt4(0))                   'vypíše "a"
        MsgBox(txt4(3))                   'vypíše "d"
        MsgBox(txt4(8))                   'vypíše "!"

Vypsání podřetězce z textu

K získání části textu máme metodu Substring. Můžeme jí předat buď dva parametry - číslo prvního znaku (začíná se od nuly) a délku požadované části textu, čímž získáme část ze začátku či z prostředka řetězce, anebo jeden jediný parametr - číslo prvního znaku (opět od nuly), což vrátí podřetězec od daného znaku až do konce. Lépe je to vidět na ukázce:

        'vypsání podřetězce z textu
        Dim txt5 As String = "Kočka přede."
        MsgBox(txt5.Substring(2, 6))      'vypíše "čka př"
        MsgBox(txt5.Substring(6))         'vypíše "přede."

Zjištění výskytu podřetězce

Metoda Contains umožňuje zjistit, jestli daný text obsahuje určitou sekvenci znaků. Pokud ano, vrátí True, jinak False. Použití je velmi jednoduché:

        'zjistíme, zda-li proměnná txt2 obsahuje daný podřetězec
        Dim txt2 As String = "Červená Karkulka potkala vlka."
        MsgBox(txt2.Contains("kulka"))    'vypíše True
        MsgBox(txt2.Contains("Kulka"))    'vypíše False, záleží na velikosti písmen

Kontrola začátku a konce řetězce

Občas potřebujeme ověřit, zda text začíná či končí nějakým podřetězcem. K tomu nám slouží metody StartsWith, resp. EndsWith. Vrací samozřejmě hodnoty True a False.

        'zjistíme, zda-li proměnná txt3 začíná a končí daným podřetězcem
        Dim txt3 As String = "*104*#"
        MsgBox(txt3.StartsWith("*1"))     'vypíše True
        MsgBox(txt3.EndsWith("*1"))       'vypíše False
        MsgBox(txt3.EndsWith("*#"))       'vypíše True

Nalezení prvního a posledního výskytu podřetězce

Metoda Contains nám vrátila, jestli text daný podřetězec obsahuje. Ale nevrátila nám jeho pozici, abychom s ním mohli dále pracovat. K tomu máme metody IndexOf a LastIndexOf. Obě vrátí pořadí prvního znaku hledané sekvence (čísluje se opět od nuly). Jak ale samotné hledání funguje? Metoda IndexOf prohledává text od začátku, metoda LastIndexOf od konce. Jakmile najdou daný podřetězec, vrátí pozici prvního znaku tohoto podřetězce. Pokud by text daný podřetězec neobsahoval, metody vrátí hodnotu -1, což musíme vzít v úvahu.

        'nalezení prvního a posledního výskytu daného podřetězce
        Dim text As String = "aBBaBBaa"
        MsgBox(text.IndexOf("BBa"))       'vypíše 1
        MsgBox(text.LastIndexOf("BBa"))   'vypíše 4
        MsgBox(text.IndexOf("c"))         'vypíše -1

Nahrazování podřetězců v textu

Často musíme nahradit určité sekvence znaků v textu sekvencí jinou, k tomu máme metodu Replace. Předáme jí to, co chceme nahradit, a to, čím to chceme nahradit, a dostaneme výsledek.

        'nahrazení všech výskytů v textu
        Dim nahradit As String = "Veverka kopla do bagru. Veverka vylezla na strom."
        MsgBox(nahradit.Replace("Veverka", "Liška"))   'vypíše "Liška kopla do bagru. Liška vylezla na strom."

Rozdělení více hodnot do pole a jejich opětovné spojení

Tuto poměrně složitou funkci má v sobě datový typ String zabudovanou. Často potřebujeme zpracovat (často užíváme termín parsovat) třeba čísla oddělená čárkami. A právě k tomuto účelu máme metodu Split. Předáme jí oddělovač, tedy znak nebo sekvenci znaků, kterými jsou jednotlivé hodnoty odděleny, a dostaneme pole stringů, které pak můžeme třeba projít cyklem.

Pokud chceme pole stringů spojit, použijeme metodu Join, předáme jí oddělovač a pole, které spojujeme, a dostaneme hodnotu typu String. Všimněte si, že všechny metody a vlastnosti jsme volali stylem <proměnná>.<metoda>, ale najednou voláme String.Join. Důvod je prostý - pole, které chceme spojit, není typu String, a samotné pole žádnou metodu Join nemá, protože tato metoda je specifická pro typ String. Metoda Join je tzv. statická metoda, což znamená, že ji nemůžeme volat přes instanci objektu (proměnnou), ale přes datový typ (String). Důvodem je to, že tato metoda nepracuje s již vytvořeným Stringem, ale vytváří nový. Podobných metod existuje hodně, již jsme se jistě setkali s metodou TryParse datového typu Integer, která bezpečně převáděla text na číslo.

        'rozdělení do pole podle oddělovače a jeho spojení
        Dim hodnoty As String = "-1,13,17,222,0.15,19"
        Dim vysl() As String = hodnoty.Split(",")
        For i As Integer = 0 To vysl.Length - 1
            MsgBox(vysl(i))               'vypíše postupně všechny čísla
        Next
        MsgBox(String.Join(";", vysl))    'spojí pole Stringů s jiným oddělovačem

Oříznutí "bílých" znaků ze začátku a konce textu

Někdy se nám stává, že text začíná tabulátorem, koncem řádku nebo mezerou, což se nám tak úplně nehodí do krámu. Ještě že máme metodu Trim, která se těchto potvůrek zbaví:

        'odstranění počátečních a koncových netisknutelných znaků
        Dim oriznout As String = " slovo  "
        MsgBox(oriznout.Trim())

Víceřádkový String

Pokud vypisujeme na obrazovku třeba MessageBox, je často vhodné pro lepší čitelnost rozdělit dlouhou větu do více řádků. Pokud trochu znáte starší verze jazyka Visual Basic, používala se konstanta vbCrLf. Tu můžeme využít i ve Visual Basic .NET, ale spíš bych se klonil k použití Environment.NewLine, protože na každém operačním systému vypadá konec řádku trošku jinak. Na Windows to jsou znaky (13 - CR a 10 - LF), na Linuxu jen znak 13 - CR a na Mac OS zase jen znak 10 - LF. Dnes sice ještě .NET aplikace nejsou plně podporovány jinde než na Windows, ale stejně je lepší s touto možností počítat, i když je to pro nás trochu více psaní.

        'konec řádku v textu
        Dim dvaRadky As String = "První řádek." & Environment.NewLine & "Druhý řádek."

Velká a malá písmena

Poslední dvě metody datového typu String, které si ukážeme, umí převádět text na velká a malá písmena. Jsou to metody ToUpper pro velká písmena a ToLower pro malá písmena. Jejich použití je velice snadné:

        'převod na velká a malá písmena
        Dim jmeno As String = "KleOFác"
        MsgBox(jmeno.ToLower())           'vypíše "kleofác"
        MsgBox(jmeno.ToUpper())           'vypíše "KLEOFÁC"

Jak na dlouhé texty a jejich skládání

V určitých situacích potřebujeme pracovat s poměrně dlouhým textem. Pokud použijeme standardní operátor &, může se stát, že aplikace bude pomalá. Pokud tedy skládáme za sebe dlouhé texty, je výhodné použít objekt StringBuilder, který je mnohonásobně rychlejší.

Vytvoříme tedy nový objekt typu StringBuilder a text přidáváme na konec metodou Append či AppendLine. Append jen připíše předaný text na konec dosavadního textu, AppendLine za přidaný text automaticky vloží konec řádku. Hotový text získáme zavoláním metody ToString.

        'poskládání dlouhého řetězce
        Dim sb As New System.Text.StringBuilder()
        For i As Integer = 1 To 500
            sb.Append("Číslo ")
            sb.Append(i)
            sb.AppendLine(".")
        Next
        MsgBox(sb.ToString())

To je pro tento díl vše. Jsem rád, že se vám seriál líbí, jak je vidět z diskusí pod články, pokud máte jakékoliv náměty na témata, kterým by se měl věnovat, určitě napište do diskuse nebo na e-mail.

Update: Doporučuji přečíst diskusi pod článkem, je v ní něco o regulárních výrazech pro složitější vyhledávání. V dohledné době o tom snad napíši další článek pro pokročilejší.

 

Minisoutěž - zadání

Dobrý programátor musí umět přemýšlet, zkuste tedy potrápit myšlení a vyřešit následující úkoly:

  1. Napište funkci, které předáte křestní jméno a funkce jej vrátí správně zapsané, tzn. první písmeno velké a ostatní malá. Pokud jí tedy předáte KLeofÁcek, musí vrátit Kleofácek.
  2. Napište funkci, které předáte cestu k nějakému souboru, a ona vám z ní vytáhne pouze název souboru s příponou. Takže pokud jí dáte C:\Složka\Podsložka\soubor.txt, vrátí soubor.txt.
  3. Napište funkci, které předáte text a ona vám jej vrátí pozpátku. Pokud jí tedy předáte Včera večer, vrátí rečev arečV.

Minisoutěž - správná řešení a připomínky

1. Jednoduchá úloha na metody Substring, ToUpper a ToLower. Připomínám, že metoda Substring má dvě možnosti použití - buď dva parametry (první znak, délka podřetězce), nebo jeden parametr (první znak). Zde se právě hodilo použít i volání s jedním parametrem, která vrátí podřetězec od prvního znaku až do konce (není nutné tedy dávat druhý parametr a kvůli tomu počítat délku řetězce a odečítat od ní jedničku, jak mnozí z vás dělali, je to sice správně, ale víc práce). Mnoho z vás také používalo StringBuilder, což není špatně, ale v tomto případě je to kanón na vrabce - slučování řetězců je jen jedno a rychlostní rozdíl bude naprosto zanedbatelný. Nejjednodušší řešení bude vypadat takto:

    Function Jmeno(ByVal jm As String)
        Return jm.Substring(0, 1).ToUpper() & jm.SubString(1).ToLower()
    End Function

2. Tato úloha měla procvičit metody Substring a LastIndexOf. Pomocí LastIndexOf si v řetězci najdeme poslední zpětné lomítko, přičteme k výsledku 1 (aby lomítko nebylo zahrnuto do výsledku, a pokud tam náhodou není a LastIndexOf vrátí -1, stane se z ní nula, čímž se vybere celý řetězec, což je také správně), a pak pouze použijeme Substring. Opt stačí předat metodě Substring pouze pozici, od které chceme text odříznout, nemusíme počítat, kolik znaků zbývá do konce řetězce. Jinak podotýkám, že pro tento účel lze použít funkci System.IO.Path.GetFileName(soubor).

    Function Soubor(ByVal fn As String)
        Return fn.Substring(fn.LastIndexOf("\") + 1)
    End Function

3. Tato funkce je o trochu složitější a asi se vyplatí použít StringBuilder, spojování řetězců bude víc. Je vhodné použít klesající For cyklus, ale jde to samozřejmě i cyklem normálním a správné pozice znaků počítat.

    Function Pozpatku(ByVal s As String)
        Dim sb As New StringBuilder()
        For i As Integer = s.Length - 1 To 0 Step -1        'cyklus jde pozpátku (díky Step -1)
            sb.Append(s(i))
        Next
        Return sb.ToString()
    End Function

Musím říci, že jsem byl překvapen počtem řešení, která mi dorazila hned během prvního dne. Rád bych poděkoval všem soutěžícím za účast.

Minisoutěž - výsledky

Došlo mi celkem 8 řešení, všechna byla správná, nejrychleji to ovšem stihnul pan Vratislav Renner, kterému posílám slibované DVD Get Ready 2 s přednáškami a materiály o .NET frameworku.

 

hodnocení článku

1 bodů / 1 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Všechny díly tohoto seriálu

18. Dědičnost 06.10.2008
17. Objektově orientované programování - základy 30.06.2008
16. Třídy a funkce .NET frameworku, o kterých je dobré vědět 31.12.2007
15. Práce se soubory, úvod do objektově orientovaného programování 19.11.2007
14. Vykreslujeme graf 31.08.2007
13. Úvod do grafiky 27.08.2007
12. Práce s textem a řetězci 17.08.2007
11. Kolekce a pole 27.07.2007
10. Funkce a procedury 01.06.2007
9. Přidáváme druhý formulář 18.05.2007
8. Pole, cykly a práce se soubory 14.05.2007
7. Pole 09.05.2007
6. Cyklus For 05.05.2007
5. Složitější podmínky a rozhodovací struktury 26.04.2007
4. Podmínky a operátory 26.04.2007
3. Proměnné a datové typy 25.04.2007
2. Začínáme programovat 25.04.2007
1. Úvod, vývojové prostředí a základní pojmy 25.04.2007

 

Mohlo by vás také zajímat

Jak na platby pomocí PayPalu

PayPal je asi nejznámější a celosvětově nepoužívanější řešení pro online platby. V tomto článku si ukážeme, jak používat REST API pro realizaci jednoduché platby.

Co je nového ve Visual Studio 2013 Update 3

Build, .NET Core 3.0 a jak to bude s .NET Frameworkem

 

 

Nový příspěvek

 

Mr.

1'

csharp|

xml|

css|


'

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Diskuse: Práce s textem a řetězci

Ahoj,

jsem úplný začátečník.

A nejspíš jsem asi nedával pozor v předchozích lekcích, ale vůbec netuším, jak danou funkci (zde napsanou v řešení) vyvolat.

nahlásit spamnahlásit spam 0 / 2 odpovědětodpovědět

Co kdyby jste naznačil, čemu nerozumíte (k jakému tématu se ptáte).

Z otázky to není patrné. Uveďte nerozumím funkci: ...

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Dobrý den, prosím vás, nevím si rady s tímto: mám pole znaků(slov), oddělených čárkou, které získám z textboxu a chtěl bych, napsat MsgBox, který by mi vypsal třeba 2. slovo z pole.

Slova nejsou pevně zadána, zadávají se do textboxu a podle toho msgbox vypisuje jednotlivá slova.

Děkuji.

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

    private void splitDemo()
    {
        string texty = "A,B,C,D,E";
        string[] textyArray = texty.Split(',');

        // System.Windows.Forms.MessageBox.Show("Test");
        // nebo nahoře: using System.Windows.Forms;
        // a pak jen MessageBox.Show("Test");
        
        foreach (string pismeno in textyArray)
        {
            MessageBox.Show(pismeno);
        }

        // Zobrazit druhé písmeno - Array začíná indexem 0 ...
        MessageBox.Show("Druhé písmeno: " + textyArray[1]); 
    }
    

Předpokládám, že naplnit String proměnnou "texty" z TextBoxu zvládnete :-)

nahlásit spamnahlásit spam -2 / 2 odpovědětodpovědět

Diskuse: Práce s textem a řetězci

Toto je naozaj užitočné, perfektné, že je to všetko na jednej stránke. Už som pridal do záložiek. Najviac sa mi páči, že je to prehľadné, nemusíte klikať na iné kategórie, proste tu je všetko o texte.

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Diskuse: Práce s textem a řetězci

Dobrý den, potřeboval bych poradit. Mám číslo např. -357/4 (první číslice může být jedno až šesti ciferná, kladná či záporná; číslice za lomítkem jenom kladná také šesticiferná max.)

Potřebuji toto číslo rozdělit na tři hodnoty. První hodnota závisí na tom, jestli je první číslo kladné nebo záporné,je-li kladné vyhodí 2, záporná 1, druhá hodnota se rovná číslici před lomítkem bez znaménka a třetí hodnotě za lomítkem. Se třemi vygenerovanými hodnotami potřebuji později pracovat.

Ocením rady i části kódu. Děkuji za pomoc.

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Diskuse: Práce s textem a řetězci

Dobrý den,

prosím Vás nezlobte se, jsem začátečník s VB a narážím na elementární věc se kterou si nevím rady. Koukal jsem že nahoře máte příklad pro výpis určité pozice řetězce. Zkoušel jsem ten kód

Private sub vypis()
'vypsání jednoho znaku z textu
Dim txt4 
 txt4 = "abcdefgh!"
 MsgBox(txt4(0))                   'vypíše "a"
 MsgBox(txt4(3))                   'vypíše "d"
 MsgBox(txt4(8))                   'vypíše "!"
end sub

a vzdy když chci spustit tak compiler říká Type MisMatch, snažím se řešit pro

blém že v promené mam načtený řetšzec oddělený "," a potřebuju vyseparovat jednolivé podřetezce kódu a následne s nimi pracovat, ale furt narážím že neumím pracovat s pozicemi toho řetězce. (řetezec je například 241564,66458,1233,123,45996...) Dokázal by mi někdo prosím pomoci, urcitě jde o nejakou hloupost ale nemám dostatek zkušeností abych daný problém vyřešil. Zkouším to pod MS Excell 2007. Předem děkuji za odpověd

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět



Sub Retezce()

' Kód VBA - MS Excel

    Dim Retezec As String, A As Long
    Dim Zasobnik As String, Znak As String
    
    Retezec = "241564,66458,1233,123,45996"
    
    ' Len(Retezec) = Délka řetězce
    For A = 1 To Len(Retezec)
        ' Mid = funkce Část viz. Excel
        Znak = Mid(Retezec, A, 1)
        
        If Znak = "," Then
            ' Vypiš Zasobnik do Debug okna (Zobrazit okno = CTRL+G)
            Debug.Print Zasobnik
            ' Vyprázdni Zasobnik po výpisu,
            ' budeme plnit dalšímy znaky
            Zasobnik = ""
        Else
            ' Přidej do zásobníku "Zasobnik" znak "Znak"
            Zasobnik = Zasobnik & Znak
        End If
        
    Next A
    
    ' Vypiš poslední Zasobnik do Debug okna
    Debug.Print Zasobnik

End Sub

 
nahlásit spamnahlásit spam 0 / 2 odpovědětodpovědět

Mnohokrát Vám děkuji. Moc jste mi pomohl.

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Diskuse: Práce s textem a řetězci

Ahoj, mám menší problém. Všechny tyto funkce, jako například replace, trim, nebo ToUpper a ToLower mi fungují při vypsání v msgboxu. Ale jakmile mám text v richtextbox1, nebo textbox, který se překope například jen na malá písmenka do richtextbox2, nebo textbox2, tak to právě nenastane a zůstane to ve stejném tvaru jako v richtextbox2. Poradí někdo? Nemůže být problém s tím, že se jedná o STRING? Díky

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Už mi to funguje

RichTextBox2.Text = promenna.Replace(neco za neco)

například... díky za tutorial

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Diskuse: Práce s textem a řetězci

Dobrý den.

Mám problém s načítáním textu z textového souboru.

1. Mám vytvořený prázdný textový soubor.

2. Do jeho prvního řádku napíšu jakýsi příkaz, soubor uložím a

odešlu jej po síti na jiný PC do určité složky.

3. Tam je spuštěná jednoduchá aplikace (ve VB 2008), Která když

tento soubor objeví, načte první řádek a zachová se podle

toho, jaký příkaz tam najde. (Provede a uloží PrinScreen,

Simuluje stisk klávesy, apod... Až potud je vše v pořádku, a

bezvadně to funguje.

Když jsem se pokusil přidat další, jednoduchou funkci. "Vypiš na obrazovce text, který jsi načetl ze zmiňovaného souboru." (MsgBox). Tak se sice text vypíše, ale veškerou diakritiku mi nahradí čtverečky.

Tady je část kódu, která dělá problémy:

        Dim soubor As New IO.StreamReader("Cmd.txt")
        Dim text As String = soubor.ReadLine()
        MsgBox(text)

Poradí mi někdo, co s tím?

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Zpět.

Omlouvám se. Už jsem na to přišel. Stačilo textový soubor uložit s jiným kódováním než ANSI...

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Diskuse: Práce s textem a řetězci

Dělám takovou jaksi šifrovací aplikaci :). A chtěl to co napíše uživatel do textbox1 aby se po stisku tlačítka změnilo na nějákou hloupost a vylezlo to v textbox2. A chtěl aby to bylo tak, že A se nakovenrtuje třeba na C nebo tak. Díky za odpoved.

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Diskuse: Práce s textem a řetězci

Dobrý den, mám malý problém. Píšu program, který mi z novinky.cz vytahne pocasi. Moje dosavadní práce vypadá takto:

Imports System.Net

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim Predpoved As String

        Dim url As String = String.Format("http://www.novinky.cz/pocasi", Now)
        Dim rg As HttpWebRequest = HttpWebRequest.Create(url)

        Dim rs As HttpWebResponse = rg.GetResponse()

        Dim sr As New IO.StreamReader(rs.GetResponseStream())
        Dim t As String = sr.ReadLine()


        While Not sr.EndOfStream
            Dim s As String = sr.ReadLine()

            If s.Contains("Dnešní předpověď") Then
                Predpoved = s.Substring(s.IndexOf("Dnešní předpověď"))
            End If

        End While
        

        sr.Close()
        rs.Close()

        Predpoved.Remove(Predpoved.IndexOf("Pranostika"), Predpoved.LastIndexOf(">") - Predpoved.IndexOf("Pranostika"))


        txtPredpoved.Text = Predpoved

    End Sub
End Class

(txtPredpoved je RichTextBox)

Problém je v tom, že mi to z neznámého důvodu neodstrani ten text za prvním výskytem slova "pranostika". Nevíte někdo, čím to může být?

Děkuji.

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Diskuse: Práce s textem a řetězci

mám textbox do kterého zapisuji text, ale nevím jak ho formátovat pomocí Fontdialogu.

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Diskuse: Práce s textem a řetězci

Dobrý den.

Měl bych prosím dosaz, jak je možné (jestli to vůbec jde) měnit v cyklu názvy objektů.

Dim i As Integer
For i = 1 To 10 step 1
    labeli.text = i
Next 

Potřebuji prostě přepsat 10 labelu a chtěl bych to ošetřit cyklem.

Ale nevím jak to správně zapsat. (Snad pochopíte o co se snažim)

Moc díky za radu

Bouda

PS: S programováním teprve začínám a tento seriál mi moc pomoh. Sice jsem jsem teprve v polovině, ale snad to dál půjde :)

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Zdravím,

o řešení tohoto úkoly se zajímám i já. Vyřešil jsem to tak, že jsem dal všechny labely do panely a přistupoval k nim jako k Panel1.Controls(index), ale po změně jména nastala chyba. Když jsem se pokusil pracovat s přejmenovaným objektem, vyhodilo mi to Error. Co s tím?

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

S tím bude trochu problém. Pokud chcete pracovat se skupinou prvků, buď je musíte vytvořit pomocí kódu (což pro začátečníky asi není jednoduché), anebo použít malý trik - vytvořit si pole Labelů.

Do procedury si dejte tento kód:

Dim Labely() As Label = {Label1, Label2, Label3, Label4, Label5, Label6, Label7, Label8, Label9, Label10}
For i As Integer = 1 To 10
    Labely(i - 1).Text = "Tohle je " & i & ". label!"
Next

Problém je totiž v tom, že název objektu se nedá "poskládat". Pokud si ale vytvoříte pole, můžete k jednotlivým prvkům přistupovat dynamicky. Pozor, prvky pole se vždy číslují od nuly, pokud tedy máte cyklus od 1, musíte odečítat jedničku!

Nápad s použitím kolekce Controls je také poměrně pěkný, problém je ale v tom, že kolekce Controls není kolemce typu Label, ale kolekce typu Control, ze kterého vychází všechny komponenty. Ale tento obecný typ Control nemá vlastnost Text, tu má až Label. Muselo by se provést tzv. přetypování, ale to už je také složitější.

nahlásit spamnahlásit spam 2 / 2 odpovědětodpovědět

pokud jsou všechny objekty (labely) předem dané

tak využívám jejich vlastnost "TAG" kam se dá "něco" zapsat

(předem - ručně), popř. při generování za běhu je to obdobné

Pak už jen projdu celou sadu Controls

kde TAG = "Moje hodnota"

For Each Prvek In Me.Controls
   If Prvek.Tag = "Toto je Label" Then
         Prvek.BackColor = NovaBarva
   End If
Next

Druhé takové "uložiště" je .ToolTipText

nahlásit spamnahlásit spam -3 / 3 odpovědětodpovědět

Dost záleží, co a jak chce člověk programově v jednotlivých controlech zpracovávat.

není to sice přesně dle přání autora, ale pokud nevadí opačná logika, dá se to třeba takto (tou opačnou logikou myslím, že nevyvolávám jednotlivé controly jménem pro zamýšlenou akci, ale naopak procházím všechny controly a provádám pro ně adekvátní akci):

 Dim labtype As New Label

        For Each cntrl As Control In Me.Controls

            If cntrl.GetType.Equals(labtype.GetType) Then
                ' náhrada funkcionality "labeli.text=i":
                cntrl.Text = cntrl.Name.Substring(5)

            End If
        Next

ten fiktivní label labtype tam mám jenom proto, protože se mi v rychlosti nepodařilo jinak filtrovat Controly typu Label.

Jinak by to mělo dělat přesně to, co měl žadatel ve své rutine s tím rozdílem, že tak nečiní pro labely 1-10, ale pro všechny labely ve formuláři. Dá se to ale omezit programově, případně všechny labely, které chci programově procházet by bylo možné zasadit do samostatného groupboxu a pak procházet jen tento.

Jinak funkcionalita plně závisí na systému pojmenování labelů, pokud necháme standardní "label"+číslo, je možné v cyklu využít i vyseparované číslo k libovolným výpočtům.

nahlásit spamnahlásit spam 0 / 2 odpovědětodpovědět

Nedalo mi to a zkusil jsem to naopak a skutečně to jde i v logice, kterou požadoval pisatel. Takže celá krása by mohla vypadat následovně:


For i As Integer = 1 To 10
  Me.Controls(Me.Controls.IndexOfKey("label" & i)).Text = "Tak toto je label " & i
Next

jinak řečeno, můžu takto pracovat i se "složenými" názvy controlů tak, že si název (jako string) prachobyčejně spojím, pak vyhledám jeho index v kolekci Controlů mého formuláře, no a pak u Controlu vyhledaného indexu nastavím požadovanou vlastnost text. (control daného jména samozřejmě musí existovat a být typu, který podporuje vlastnost text).

nahlásit spamnahlásit spam 0 / 2 odpovědětodpovědět

Ano, toto řešení funguje také. Má však jeden háček - je poměrně pomalé. Pokud pracuji s deseti Labely, tak je to celkem jedno. Pokud jich ale bude 150, už to nebude stát za moc.

Můj příklad s polem není úplně nejpraktičtější na použití, ale co se týče výkonu, bude to nejlepší.

Ve skutečnosti se tento problém většinou řeší tak, že se na formulář Labely nenaklikají, ale vytvoří pomocí kódu:

Dim Labely(9) As Label
For i As Integer = 0 to 9
    Labely(i) = New Label()  'vytvořit instanci objektu komponenty Label
    Labely(i).Top = 20       'nastavit pozici komponent
    Labely(i).Left = 100 * i
    Labely(i).Text = "Můj Label " & i   'nastavit text
Next
Me.Controls.AddRange(Labely)   'přidat na formulář vytvořené komponenty

Není dobré zvykat si na hledání komponent, funkcí a procedur pomocí názvů, vede to ke špatným návykům v budoucnu. V ojedinělých případech je to nezbytné, ale v naprosté většině to jde řešit jinak - čistě.

nahlásit spamnahlásit spam 0 / 2 odpovědětodpovědět

Rád bych využil Vašeho námětu a poprosil o doplnění v oblasti, kterou jsem kdysi potřeboval řešit (není to sice na téma článku, ale navazuje to na Váš poslední příspěvek).

To co píšete je, samozřejmě, pravda a já měl skutečně na mysli běžný formulář s několik málo prvky - zkrátka v duchu pisatelova dotazu (jsem si vědom i toho, že Vámi popsaný problém je dokonce hlubší, protože mohu sice pracovat pouze s deseti labely, ale pokud na formu bude 100 jiných prvků, pak se ta pomalost řešení projeví zřejmě úplně stejně).

Ale k Vašemu "programovému" řešení. Je možno nějak si "programově" pomoci i u handlerů? Nevím, jestli se vyjadřuji správně, ale celkem běžně se mi stává, že mám na formuláři několik prvků jejichž nějaká událost je zpracovávána stejně či podobně. Konkrétní příklad. Mám třeba na stránce několik checkboxů. Běžně reaguji na změnu jejich stavu

Private Sub CheckBox1_CheckedChanged(ByVal sender As System.Object, _
 ByVal e As System.EventArgs) Handles CheckBox1.CheckedChanged
' ošetření změny stavu
    End Sub

Jenomže mám-li těch checkboxů více a jejich funkcionalita spolu souvisí (ošetřuji ji stejnou funkcí) musím napsat buďto:

Private Sub CheckBoxs_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) _
     Handles CheckBox1.CheckedChanged,CheckBox2.CheckedChanged,...
' ošetření změny stavu
    End Sub

nebo pro každý checkbox napsat samostatné ošetření události změny stavu a z něj volat společnou funkci.

Není možné, pokud si jednotlivé controlls vytvořím programově dle Vašeho příspěvku, nějak tyto controlls ošetřit taky společně?

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Napojit na událost lze samozřejmě i dynamicky vytvořené komponenty:

AddHandler CheckBoxes(0).CheckedChanged, AddressOf Checkboxes_CheckedChanged 

Záhlaví a deklaraci události musíte mít stejné. Pokud chcete v události zjistit, který konkrétní checkbox událost vyvolal, dostanete se k němu přes přetypování na typ komponenty (v tomto případě na CheckBox).

Dim chkBox As CheckBox = CType(sender, CheckBox)
nahlásit spamnahlásit spam 0 / 2 odpovědětodpovědět

Dík - je to sice asi základ, ale je to přesně to, co jsem potřeboval a funguje to.

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Asi jsem dnes natvrdlý :-D

'  Takže jsme vytvořili sadu CheckBoxů

Dim CheckBox(9) as CheckBox

For A = 0 To 9
CheckBox(A)= New CheckBox() 

' AddHandler sem ? - verze A/
 
Next

'  a nebo sem ? - verze B/
'  a pak se přidá globálně AddHandler kdy 0 (nula)
'  je odkaz na celou sadu ???

AddHandler CheckBox(0).CheckedChanged, AddressOf Checkboxes_CheckedChanged 
 


'  a přetypování jsem nepochopil vůbec :-(

Dim chkBox As CheckBox = CType(sender, CheckBox)

'  nešlo by napsat krátký příklad (dvě procedury)
'  AddHandler a Sub Handler 
'  pls díky

nahlásit spamnahlásit spam 0 / 2 odpovědětodpovědět
dim cb(9) as checkbox
for i as integer = 0 to 8
addhandler cb(i).checkedchanged, addressof Checkboxes_CheckedChanged 
next

' .....

private sub checkboxes_checkedchanged(byval sender as object, byval e as eventargs)
dim cb as checkbox = ctype(sender, checkbox) ' sender je objekt, u každé procedury hlídající událost jsou argumenty (e) a pak sender, na němž událost nastala. sender je však objekt, takže z něj musíme udělat checkbox
if cb.checked then msgbox(cb.name & " je označen")
end sub
nahlásit spamnahlásit spam 2 / 2 odpovědětodpovědět

Moc děkuji :-)

nahlásit spamnahlásit spam 0 / 2 odpovědětodpovědět

Dvě otázky:

A/ v proceduře checkboxes_checkedchanged nefunguje předání parametru Sender.Name

B/ V této diskuzi jsme zmiňovali výhodu umístnit komponenty na Panel, nepřišel jsem na to jak komponenty (sadu) umístnit na existující Panel_Vstupy , nebo musí být panel vytvořen za běhu?

 Private Sub Add_CheckBox()
        Dim cb(9) As CheckBox
        For i As Integer = 0 To 8
            cb(i) = New CheckBox      'vytvořit instanci objektu komponenty Button
            cb(i).Top = 290           'nastavit pozici komponenty
            cb(i).Left = (i * 46) + 5   'nastavit pozici komponenty
            cb(i).Width = 45
            cb(i).Text = "IN" & i   'nastavit text
            AddHandler cb(i).CheckedChanged, AddressOf checkboxes_checkedchanged
        Next

        Me.Controls.AddRange(cb)
        ' .....
    End Sub

    Private Sub checkboxes_checkedchanged(ByVal sender As Object, ByVal e As EventArgs)
        Dim cb As CheckBox = CType(sender, CheckBox) ' sender je objekt, u každé procedury hlídající událost jsou argumenty (e) a pak sender, na němž událost nastala. sender je však objekt, takže z něj musíme udělat checkbox
        If cb.checked Then MsgBox(cb.name & " je označen")
    End Sub

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

U jedničky se žádný název nezobrazí, protože při vytváření komponenty za běhu jste jí žádný název nenastavil.

Co se týče druhého problému, každá komponenta, která může obsahovat v sobě nějaké komponenty (Form, Panel atd.) má kolekci Controls, do které musíte komponentu přidat. Pokud tedy chcete přidat komponentu na Panel, musíte napsat např.:

Panel1.Controls.AddRange(b)

Když dáte Me.Controls.AddRange(cb), tak Me odkazuje na aktuální formulář.

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Díky za radu, funguje to korektně

Tady je výsledný kód korektně

s přidáním zjištění pořadového ID prvku na PANELU

Ještě jednou díky, ať poslouží všem

   Private Sub Add_CheckBox()
        Dim cb(9) As CheckBox
        For i As Integer = 0 To 8

            cb(i) = New CheckBox            'vytvořit instanci objektu komponenty 
            cb(i).Name = "ch_Vstup_" & i    'definovat jméno komponenty
            cb(i).Top = 10                  'nastavit pozici komponenty
            cb(i).Left = (i * 46) + 5       'nastavit pozici komponenty
            cb(i).Width = 45
            cb(i).Text = "IN" & i           'nastavit text  Panel_Vstupy

            AddHandler cb(i).CheckedChanged, AddressOf checkboxes_checkedchanged    'odkaz na událostní proceduru
        Next

        Panel_Vstupy.Controls.AddRange(cb)  'vložit prvky na PANEL Panel_Vstupy

    End Sub

    Private Sub checkboxes_checkedchanged(ByVal sender As Object, ByVal e As EventArgs)

        Dim cb As CheckBox = CType(sender, CheckBox) ' sender je objekt, u každé procedury hlídající událost jsou argumenty (e) a pak sender, na němž událost nastala. sender je však objekt, takže z něj musíme udělat checkbox
        Dim ch_ID As Integer = Panel_Vstupy.Controls.IndexOfKey(cb.Name)
        If cb.Checked Then MsgBox("Je označen:  " & cb.Name & vbCrLf & "Index prvku: " & ch_ID)

    End Sub

nahlásit spamnahlásit spam 0 / 2 odpovědětodpovědět

Diskuse: Práce s textem a řetězci

Tak jsem tu ve 23:59:59 a ono nic.

nahlásit spamnahlásit spam -2 / 4 odpovědětodpovědět

Netvrdím, že patřím ke tvorům, kteří by v tuto denní dobu spali, ovšem zpracování výsledků chvíli trvá. Už jsou k dispozici.

nahlásit spamnahlásit spam 2 / 4 odpovědětodpovědět

Diskuse: Práce s textem a řetězci

Tvořím aplikaci, která načte text ze souboru a načte také slovník, ve kterém jsem uvedena slova, která budou nalezena &/& slova která za ně nahradíme.

Procházím tedy text a postupně na něj aplikuji změnu pomocí

CelyText = CelyText.Replace(naleznute.Trim(), nahrazene.Trim())

Je zde ale jedna nepříjemnost, která se mi nepodařila vyřešit. Jenda se o náhradu slova uvnitř řetězce.

Např:

ve slovníku: ne/no

v textu: One se přepíše na Ono

Chtěl bych tuto věc oštřit, ale nepřišel jsem na to jak.

Děkuji.

Lukáš Fejta

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Ideální by byla možnost nastavení parametru vyhledávání na "úplnou shodu". To však zřejmě metoda IndexOf neumožňuje (alespoň o tom nevím).

Napadá mě pouze připojit ještě kromě vyhledávání porovnání délky slov.

Pokud by se délka slova nalezeného ve slovníku lišila od délky hledaného slova pak by muselo proběhnout hledání dalšího výskytu hledaného slova od následující položky.

Toto by vyžadovalo procházení slovníku cyklem, kde by v těle cyklu (pokud by se délky slov lišily) docházelo ke změně parametru startIndex (nalezený index + 1) a to do té doby dokud by nebylo nalezeno příslušné slovo nebo bylo dosaženo konce slovníku.

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Ještě mě napadla jedna věc - použít tzv. regulární výrazy (někdy o tom snad napíšu článek, je to ale trochu složitější). Velmi zjednodušeně - pokud budu hledat přes regulární výrazy v textu abc bc d text bc, najde mi to dva výskyty. Pokud chci, aby to, co najdu, byl začátek slova, přidám před hledaný výraz \b. Takže pokud budu hledat \bbc, najde mi to jen to prostřední bc. Když dám \b i na konec, najde mi to jenom samostatné slovo bc. Pokud nám nesejde na velikosti písmen ve výsledném textu, nahrazení bude vypadat takto (nahrazuji slovo za slovíčko):

Dim RegExp As String = "\bslovo\b"
TextBox4.Text = System.Text.RegularExpressions.Regex.Replace("první slovo se nahradí, druhéslovo už ne", RegExp, "slovíčko", System.Text.RegularExpressions.RegexOptions.IgnoreCase)

Tím, že poslední parametr je IgnoreCase jsme řekli, že i když hledáme slovo, bude vyhovovat i Slovo. To pak ale budeme pravděpodobně chtít, aby i nahrazené slovo zachovalo první velké či malé písmeno. Pokud tedy nalezené slovo začíná velkým písmenem, dosadíme za něj nové slovo začínající také velkým písmenem, pokud nalezené slovo bude začínat malým, dosadíme za něj nové slovo začínající také malým písmenem.

To už ale regulární výraz sám nezvládne, musíme mu místo nového slova předat tzv. delegáta a napsat funkci, která podle situace vrátí to, co se dosadí.

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim RegExp As String = "\bslovo\b"
        Dim vysledek As String =  System.Text.RegularExpressions.Regex.Replace("první slovo se nahradí, druhéslovo už ne, ale třetí Slovo bude začínat velkým písmenem", RegExp, New System.Text.RegularExpressions.MatchEvaluator(AddressOf Nahrada), System.Text.RegularExpressions.RegexOptions.IgnoreCase)
    End Sub

    Function Nahrada(ByVal match As System.Text.RegularExpressions.Match) As String
        Dim noveSlovo As String = "slovíčko"
        If Char.IsUpper(match.Value(0)) Then    'pokud začíná velkým písmenem, zvětšit první písmeno a ostatní nechat malá
            Return Char.ToUpper(noveSlovo(0)) & noveSlovo.Substring(1).ToLower()
        Else   'jinak vrátit celé slovo malými písmeny
            Return noveSlovo.ToLower()
        End If
    End Function

Tohle složitější nahrazování v textu by mělo uspět a správně zohlednit počíteční velká či malá písmena. O regulárních výrazech snad někdy napíšu.

Ještě drobná poznámka - v článku má String metodu ToUpper a ToLower. Pokud ale dáme stringovaPromenna(1), výsledkem není datový typ String, ale Char (jeden znak). Ten sice tyto metody má také, ale statické, tzn. musíme je volat přes Char.ToUpper a ne přes proměnná.ToUpper.

Doufám, že používáš Visual Basic .NET, protože ve VB6 to takhle nepůjde. Pokud používáš VB.NET, zvykni si používat nativní .NET funkce proměnná.Replace a proměnná.Trim místo zastaralých Replace(proměnná, co, čím) atd. Jsou to jen přežitky z VB6, které sice fungují také, ale jsou pomalejší. Je dobré se jim vyhnout.

nahlásit spamnahlásit spam 3 / 5 odpovědětodpovědět

Děkuji moc za vyčerpávající odpovědi. O regulárních výrazech jsem opravdu nic nevěděl. Jen jsem ještě zjistil jednu věc. Možná se již nemusí vytvářet funkce Nahrada. Zjistil jsem, že aby rozlišoval velikost písmen, stačí na konci

System.Text.RegularExpressions.RegexOptions.IgnoreCase

neuvádět IgnoreCase, ale dát tam místo toho .None

Ještě jednoum moc děkuji.

PS: Ano používám Visual Basic .NET :-)

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Ještě jsem zjistil problém při nahrazování slov např. se znakem "+"

LF

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

A kde ve verzi s funkcí náhrada je místo pro zadání náhrady za "slovo"?

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Bylo to TextBox3.Text, ale nahradil sem to za proměnnou noveSlovo, aby to nebylo matoucí.

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Protože + je v regulárních výrazech speciální symbol. Pokud chceš nahrazovat speciální znaky, dej před ně zpětné lomítko.

Takže nahrazení 1+1 by vypadalo \b1\+1\b.

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Super. Díky.

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback