znak chr(0) v proměnné char či string   zodpovězená otázka

VB.NET

Zdravím.

Používám VBNET 2005 EE. .NET Framework 2.0.

Mohl by mně někdo vysvětlit, proč, když do proměnné typu char vložím přiřazením znak nula tedy: Dim ch As char=chr(0) , tak proměnná nabývá hodnoty Nothing? Povolený rozsah hodnot pro funkci chr() je přece 0..255. Stejně tak když se tento znak objeví v nějaké proměnné typu String, přestanou fungovat metody Contains, IndexOf a asi i další. Lze tuto skutečnost obejít jinak než pracovat s polem bajtů?

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

Dobrý den, zkuste to takto:

 Dim znak As Char = CChar(CStr(0))

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

Nesmysl. Toto vám převede znak 0 na Char. Tazatel žádá převedení ordinální hodnoty znaku na znak samotný.

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

Ja vím, všiml jsem si toho až potom co jsem to napsal (přečetl jsem si opakovaně příspěvek tazatele).Celkem by bylo dobré kdyby registrovaný uživatel měl možnost svůj hloupý příspěvek smazat (pokud by na něj již nebylo reagováno). Celkem bych to uvítal.Snad se to v novém Vbnetu objeví.

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

Povolený rozsah hodnot pro Char je 0-65535, uvědomte si, že ve Frameworku je všechno Unicode. Váš problém je v tom, že 0 je ve skutečnosti prázdný znak definovaný jako Nothing, viz. Char.MinValue. Tedy musíte řešit polem Charů nebo polem Integerů.

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

Tušil jsem, že nebude úniku. Abych to osvětlil, udělal jsem si dle pěkného příkladu "Kreslící tabule" prográmek, který čte a parsuje data, přicházející po sériové lince. Funkci System.Text.Encoding.ASCII.GetString() jsem nahradil StringBuilderem a cyklem, abych tam dostal i čísla, která nemají reprezentaci v příslušné převodní funkci. Všechno bylo perfektní, dokud po lince nezačaly chodit bajty 00H, které pak veškeré operace nad vytvořeným Stringem zničí. Zatím to řeším tak, že při vytváření stringu je překódovávám na znaky 20H (mezery), protože stejně nemají žádný význam, ale takhle to nemusí být vždy. Na typu String se mi líbí, že má Metody IndexOf(nějaký String), Contains(nějaký string), SubString, kterým lze elegantně oříznout začátek a prostým přičtením udělat Append příchozích dat na konec Stringu. Navíc se vůbec nemusím starat o žádné hranice.

Toto vše si asi budu muset naimplementovat ve vlastní třídě nad nějakým interním bajtovým polem...

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

String se nepoužívá na binární data, nulové znaky by mu sice principiálně vadit neměly, ale vřele doporučuji používat pole bajtů a k němu určené readery, například BinaryReader a BinaryWriter.

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

Přešel jsem na pole bajtů, BinaryReader mě nenapadl, navíc neumím posoudit v čem by mně pomohl. Data z portu čtu přes metodu podkladového streamu objektu SerialPort - SerialPort.BaseStream.BeginRead(mBuffer, 0, mBufferSize, acCtiDataCom, Nothing), takže binární data mám v poli bajtů mBuffer. Pro vlastní parsování přečtených dat by mně objekt BinaryReader asi nijak nepomohl.

Napsal jsem si vlastní třídu, která provádí operace Append, IndexOf, Remove a GetBytes nad interním bufferem, abych dosáhl obdobné funkcionality, jakou mám u typu String už vestavěnou. Používám tam ale často ReDim a Copy, což jsou zřejmě záležitosti plýtvající pamětí a procesorovým časem. Pro moje potřeby (pole o desítkách, max. stovkách bajtů) to zatím vyhovuje. Přikládám kód, pokud tam okem odborníka objevíte nějakou zradu, která by mohla časem něco způsobit, či možnost udělat něco elegantněji dejte prosím vědět.

Public Class bArray
  'Třída poskytující základní vyhledávací a kopírovací operace nad polem bajtů
  Private mBuf() As Byte 'Interní buffer

  Public Sub New() 'Prázdný konstruktor
  End Sub

  Public ReadOnly Property Length() As Integer
    'Vlastnost zproetředkující aktuální délku interního bufferu
    Get
      Length = mBuf.Length
    End Get
  End Property

  Public Function IndexOf(ByVal What As Byte(), ByVal From As Integer) As Integer
    'Funkce vrací počáteční index prvního výskytu bajtového vzoru zadaného parametrem What v interním bufferu. Hledání počíná na indexu From
    Dim Nalezen As Boolean = False
    Dim IndexPrvnihoByte As Integer = System.Array.IndexOf(mBuf, What(0), From) 'Hledám první bajt v poli, který odpovídá prvnímu bajtu v hledaném vzoru
    Do While IndexPrvnihoByte > -1 And IndexPrvnihoByte + What.Length <= mBuf.Length 'Cyklus přes všechny výskyty - prvni bajt vzoru v poli nalezen a v mBuf zbývá místo pro celý vzor
      Nalezen = True 'Kdyby byl vzor jednobajtový tak je to on
      Dim J As Integer = 1
      Do While J < What.Length 'Cyklus přes zbývající bajty vzoru 
        If mBuf(IndexPrvnihoByte + J) <> What(J) Then 'Není shoda na pozici J vzoru, končím
          Nalezen = False
          Exit Do
        End If
        J += 1
      Loop
      If Nalezen Then Exit Do 'Byla shoda ve všech pozicích vzoru, nalezl jsem.
      IndexPrvnihoByte = System.Array.IndexOf(mBuf, What(0), IndexPrvnihoByte + 1)
    Loop
    If Nalezen Then IndexOf = IndexPrvnihoByte Else IndexOf = -1
  End Function

  Public Function GetBytes(ByVal From As Integer, ByVal Count As Integer) As Byte()
    'Fuunkce vrací bajtové pole vzniklé kopií interního bufferu od pozice From a délce Count
    Dim pBuf(Count - 1) As Byte 'Nadefinuji pomocný buffer potřebné velikosti
    System.Array.Copy(mBuf, From, pBuf, 0, Count) 'Zkopíruji do něj příslušnou část pole
    GetBytes = pBuf 'Vrátím hodnotu
  End Function

  Public Sub Append(ByVal ibuf As Byte(), ByVal Count As Integer)
    'Metoda přidá z bajtového pole zadaného parametrem ibuf Count bajtů na konec interního bufferu
    Dim mBufIndex As Integer 'Index v interním bufferu
    If mBuf Is Nothing Then 'Buffer ještě nebyl inicializován
      mBufIndex = 0
      ReDim mBuf(mBufIndex - 1)
    Else
      mBufIndex = mBuf.Length
    End If
    ReDim Preserve mBuf(mBuf.Length + Count - 1) 'Dimenzuju interní buffer přesně na potřebnou velikost se zachováním obsahu
    System.Array.Copy(ibuf, 0, mBuf, mBufIndex, Count) 'Zkopíruju přidávané bajty do interního bufferu
  End Sub

  Public Sub Remove(ByVal Count As Integer)
    'Metoda odstraní ze začátku interního bufferu Count bajtů a zredukuje interní buffer na aktuální velikost
    Dim pBuf(mBuf.Length - Count - 1) As Byte 'Nadefinuji pomocný buffer potřebné velikosti
    System.Array.Copy(mBuf, Count, pBuf, 0, mBuf.Length - Count) 'Odložím do něj data, která mají v interním bufferu zůstat
    ReDim mBuf(mBuf.Length - Count - 1) 'Dimenzuju interní buffer přesně na potřebnou velikost a vymažu obsah
    System.Array.Copy(pBuf, 0, mBuf, 0, mBuf.Length) 'Zkopíruju data, která mají v interním bufferu zůstat z pomocného bufferu zpět
  End Sub

End Class

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

Podlě mě je to celkem v pohodě, akorát bych být vámi nadimenzoval ten vnitřní buffer třeba na 64kB a v případě nedostatknu místa bych ho dvakrát zvětšil. Stačí si jen pamatovat, kolik bajtů z něj je použito.

Při mazání je zbytečné ho zase zmenšovat, jakmile už si jednou paměť nabere, je neefektivní se jí zbavovat. I kdyby si to mělo vzít 20MB paměti, to přece nevadí, paměti máme dneska dost, tak proč ji nevyužít.

Jinak v případě zvětšování pole nepoužívejte Redim Preserve, ale vytvořte si nové 2x větší pole, zkopírujte to přes Array.Copy a nahraďte původní pole tím novým.

Dim newBuf(mBuf.Length * 2 - 1) As Byte    'nové pole 2x větší
Array.Copy(mBuf, 0, newBuf, 0, mBuf.Length)  'zkopírovat staré do nového
mBuf = newBuf     'zahodit staré pole a místo něj dát nové (o staré se postará GC, je to daleko efektivnější než to, co tam máte - Redim a druhé Array.Copy)

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

Díky za věcnou připomínku. Udělal jsem to dle Vašeho doporučení a funguje to OK. Jenom ten buffer nezvětšuju s ohledem na charakter úlohy na dvojnásobek, ale jen o to co mi chybí. Ono na to stejně vlastně nikdy asi nedojde, protože dat přichází relativně málo a dimenzace pole je více než dostatečná, ale musí to tam být, kdyby něco.

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

Záleží na konkrétní situaci, to zvětšení na dvojnásobek má svůj logický důvod - pokud budete mít smůlu, tak máte pole plné, přijde jeden bajt a tím pádem se musí zvětšit. Pokud jej zvětšíte na dvojnásobek, máte dost velkou rezervu, pokud jej zvětšíte jen o ten jeden bajt, za chvíli přijde další a celá šaráda se opakuje. Dvojnásobek je takové optimum mezi tím, aby se zbytečně neplýtvalo pamětí a aby se nezvětšovalo moc často (protože to je časově ta nejnáročnější operace).

Pokud ve vaší aplikaci tyhle věci nenastávají, stačí to takhle, já osobně ale raději píšu kód, který můžu využít i jinde a v jiné situaci, takže je podle mě dost často lepší psát co nejobecněji. Když už zvětšujete, je jedno, jestli o 100 bajtů nebo o 1000, překopírovat musíte stále stejný počet položek a režie mezi vytvořením pole různých velikostí je skoro stejná. Jde hlavně o to, kolikrát kopírování položek budete provádět.

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

S tou obecností máte pravdu. Také se o to snažím, ale jelikož nemám žádné programátorské vzdělání, tak se mi to ne vždy daří...

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.
  • 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