Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: VB6: Merkit tavuiksi ja takas

Sivun loppuun

setä [09.04.2007 16:51:03]

#

Tuon voksaa-tehtävän myötä kokeilin merkkijonon muunnosta Byte-taulukkoon ja hämmästyin. Testikoodi:

Option Explicit

Dim b() As Byte, i As Integer

Private Sub Text1_KeyPress(KeyAscii As Integer)
  If KeyAscii = 13 And Text1 > "" Then
    ReDim b(Len(Text1) - 1)
    b = Text1
    Debug.Print b
    For i = 0 To UBound(b)
      Debug.Print b(i);
    Next
  End If
End Sub

Tulokseksi sain:

Text1
84 0 101 0 120 0 116 0 49 0

Ja kysymys: Mistä nuo nollat tuonne tupsahti?

Merri [09.04.2007 17:05:19]

#

Visual Basicin merkkijonot ovat BSTR/Unicode-muodossa. Tämä tarkoittaa sitä, että VB käsittelee merkkejä kaksitavuisina. Kun muunnat stringin suoraan bytetauluksi, niin joka toinen merkki on useimmilla merkeillä nolla. Ja kyllä, stringiin mahtuu ihan mikä vaan Unicode-merkki (onnistuu ChrW$:llä).

Sama sitten kun muutat bytetaulua takaisin stringiksi, kaksi tavua muuttuu yhdeksi merkiksi.

Tallennettaessa tekstitiedostoa ja kutsuttaessa API-kutsuja VB muuttaa pellin alla stringit ANSI-muotoon. Vastaavasti ne muutetaan taas Unicode-muotoon paluuarvoissa. Eli aika paljon kaikkea ylimääräistä näkymätöntä toimintaa. Ei tarvitse ihmetellä, miksi stringit ovat hitaita.

StrConv() ja vbUnicode ja vbFromUnicode osaa suorittaa sitten helposti muunnoksia toisinkin tavoin.

Grez [09.04.2007 17:11:31]

#

Tosin jos haluaa käyttää Unicode WinAPIkutsuja niin sekin kyllä onnistuu, silloin vaan pitää antaa stringi sen oikeana pointterina (muistaakseni strptr)

Tuota ei tietenkään ihan varauksetta voi suositella. VB periaatteessa tekee parhaansa estääkseen käyttäjää ampumasta itseään jalkaan muistivuotojen, puskuriylivuotojen yms. osalta. Käyttämällä tuollaisia edellä mainittuja kikkoja saa sen varmistimen pois päältä ja varpaat on taas vaarassa.

Merri [09.04.2007 17:12:35]

#

Jep, ja API-kutsua täytyy muuttaa siten että Stringin tilalle vaihtaa Longin.

setä [09.04.2007 19:01:47]

#

Tutkailin että mikä tuossa hidastaa merkkijonon muunnosta byte-taulukoksi ja huomasin että viive tulee käytännössä yksinomaan merkkijonon lukemisesta string-muuttujaan. Voksaa-kantasanojen lukeminen string-muuttujaan vie yksistään 1,7 sek ja suunnilleen sama aika meni muunnoksineen.

Grez [09.04.2007 19:08:42]

#

Itselläni mystisesti ensimmäisellä kerralla testatessa lukeminen string-muuttujaan vei myös noin 1,7 sekuntia, mutta seuraavalla kerralla alle 0,1 sekuntia.

Käytännössä hitauden aiheuttanee se, että se olettaa saavansa varmaan jotain luokkaa 500-1000 merkkiä ja varaa muistia aluksi vain sille. Sitten se lukee pätkissä ja aina kasvattaa varattua muistia ja joka välissä joutuu kopsimaan sen jo luetun uuteen muistipaikkaa. Yleisesti ottaen joskus täytyy hieman "opastaa" VB:tä ja varata suoraan oletettavissa oleva määrä tilaa.

Sinänsähän tämän koko vokaalisaaritehtävän pystyisi mainiosti tekemään niin, että mitään kieltä ei tarvitsisi ladata kerralla muistiin vaan sen voisi ajaa täysin streamina. Veikkaisin että joku 100 tavua käyttömuistia riittäisi aivan mainiosti (ja koodin kuluttama muisti päälle)

Itse lataan sen koko kantasanan kerralla muistiin, mutta syynä on tosiaan lähinnä ohjelmoijan (minun) laiskuus.

Merri [09.04.2007 22:48:39]

#

Tässä taannoin tehtiin tiedostonluku, jossa vertailtiin onko kaksi tiedostoa samanlaisia vaiko ei. Tällaisessa tilanteessahan joutuu vertailemaan jokaikisen bitin lävitse molemmista tiedostoista.

Kun käytössä oli 800 Mt tiedosto, meni tiedoston ensiluvussa jotakin 40 sekuntia, mutta tämän jälkeen se löytyi muistista cachesta (en tiedä millä logiikalla, mutta löytyi kuitenkin), jolloin eri tapojen todelliset nopeudet tulivat varsin hyvin esille. Long-taulukkomuuttujalla vertailua suorittamalla tiedoston täysvertailussa kesti 1,5 sekuntia, tästä seuraavana tuli bytetaulu, jolla kesti 3 sekuntia. String laahasi viimeisenä perässä 8 sekunnilla. Ja kyseessähän oli yhteensä 1,6 Gt keskenään vertailtua dataa, joka vertailtiin 16 kt blockeissa. Koodi olisi tietty pysähtynyt mikäli eroavaisuus olisi löytynyt.

Tältä pohjalta voi tehdä jo johtopäätöksiä sen suhteen, mitä muuttujatyyppiä kannattaa tilanteen salliessa käyttää. Testikoodinpätkät olivat niin identtistä kuin oli mahdollista toteuttaa.

Tämä nyt oli offia noin kysymyksen osalta, mutta liittyy osaltaan aiheeseen.

Grez [09.04.2007 23:12:51]

#

Luonnollisesti kannattaa käyttää mahdollisimman suurta (siksi 32bit vertailu nopeampi kuin 8bit vertailu) ellei sitten tieto ole sellaista että sitä täytyy vertailla pienemmissä lohkoissa (kuten tässä ohjelmassa). Stringin käyttäminen binäärivertailussa on täysin turhaa.

Ja tosiaan, yllättäen seuraava lukukerta on nopeampi kun tiedosto on levycachessa. Itselläni oli kuitenkin joku kummallisuus koska olin lukenut samaa tiedostoa jo monta kertaa ja nopeus parani "itsestään" jollain 10. kerralla. Saattoi toki olla että muutin jotain kääntäjäparametria tms.

feenix [17.04.2007 16:40:45]

#

Merri kirjoitti:

Visual Basicin merkkijonot ovat BSTR/Unicode-muodossa. Tämä tarkoittaa sitä, että VB käsittelee merkkejä kaksitavuisina. Kun muunnat stringin suoraan bytetauluksi, niin joka toinen merkki on useimmilla merkeillä nolla. Ja kyllä, stringiin mahtuu ihan mikä vaan Unicode-merkki (onnistuu ChrW$:llä).

(Tänään on nipopäivä :)

Itse asiassa UTF-16 -muodossa. Se on vain yksi Unicoden esitysmuoto, Unicode itsessään ei ole muu kuin merkkitaulukko.

Merri kirjoitti:

Tallennettaessa tekstitiedostoa ja kutsuttaessa API-kutsuja VB muuttaa pellin alla stringit ANSI-muotoon. Vastaavasti ne muutetaan taas Unicode-muotoon paluuarvoissa. Eli aika paljon kaikkea ylimääräistä näkymätöntä toimintaa. Ei tarvitse ihmetellä, miksi stringit ovat hitaita.

NT:n kerneli kylläkin on UTF-16:a kokonaan, eli API-kutsuissa muunnoksia ei tarvitsisi tehdä. En tiedä tekeekö VB ne silti aina jotta sama koodi toimisi myös 9x-sarjassa ilman laajennoksia. Levylle kirjoittamisessa tietysti jos halutaan 8-bittisiä joudutaan muuntamaan.

Mutta UTF-16 ei ole ongelma stringeissä yleensä, VB:ssä ehkä voi ollakin. Joku debugannee binääriä ja tutkii? ;)

Grez [17.04.2007 18:20:27]

#

feenix kirjoitti:

Mutta UTF-16 ei ole ongelma stringeissä yleensä, VB:ssä ehkä voi ollakin. Joku debugannee binääriä ja tutkii? ;)

Ei se ole mikään ongelma VB:ssä. Ongelma siitä tulee siinä vaiheessa kun yrittää tunkea esim. UTF-16 muodossa olevan tavaran ilman muunnosta UTF-8 muodossa olevaan taulukkoon ja olettaa saavansa järkeviä tuloksia tai päinvastoin. Sama on kaikissa kielissä.

Merri [18.04.2007 07:50:28]

#

feenix: pakkohan se on nipoa täydentää lisänipolla!

Windowsissa on kahdet versiot monista API-kutsuista, ANSI-versiot ja Wide-versiot. On kuitenkin mahdotonta tietyissä tapauksissa automaattisesti tietää, ottaako funktio vastaan UTF-16 vai ANSI -syötettä, joten VB:n tekijät päättivät taaksepäinyhteensopivuuden takia suorittaa aina muunnoksen ANSIin automaattisesti API-kutsujen kohdalla, kuin myös tiedostoja kirjoitettaessa. Stringithän muuttuivat VB4:n kohdalla UTF-16 -muotoon, kun ne muistaakseni vielä VB3:ssa olivat muussa kuin BSTR-muodossa. Jos muunnoksia ei olisi tehty, olisi aika paljon vanhaa VB-koodia mennyt rikki, ja tämä haluttiin välttää.

Windowsissa on siis taas itsessään taaksepäinyhteensopivuuden vuoksi kahdet versiot funktioista. 9X Windowseissa tuki Wide-funktioille oli tosin varsin puutteellinen, ne olivat mukana lähinnä näön vuoksi.

VB:ssä ja Windowsissa UTF-16 toimii automaattimuunnoksilla tietyissä tapauksissa, eli tarkalleen ottaen siinä on otettu kulloinkin käytössä oleva merkistökoodaus huomioon, siis vaikkapa ISO-8859-1. Erot huomaa Asc/AscW sekä Chr$/ChrW$ -funktioiden välillä: joidenkin merkkien kohdalla arvoksi tallentuukin oikeasti eri arvo kuin mitä funktiolle on syöttänyt tai luullut syöttävänsä.


ANSI on Microsoftin itse käyttämä yhteisnimitys SBCS ja DBCS -merkistökoodauksista. Samoin käyttävät usein Wideä ja Unicodea kun tarkoittavat UTF-16:tta (eli sinällään niposi kuuluisi mennä jopa Microsoftille asti).

SBCS = Single Byte Character Set (useimmat länsimaiset ja vähämerkkiset merkistöt)
DBCS = Double Byte Character Set (monet itämaiset merkistöt)

Sitten on vielä noiden lisäksi olemassa muitakin merkistöjärjestelmiä, mm. Kiinalla on parikin omaa systeemiä, joista he ovat yhden ajaneet sisään ja pakottaneet esimerkiksi Microsoftin lisäämään Windowsiin, vanhemmat Windowsit menivät myyntikieltoon ennenaikojaan. Mutta sekin menisi siis Microsoftin näkökulmasta ANSI-nimityksen alle kun ei Unicodesta ole kysymys.


Saa nähdä jäikö tähän virheitä, ulkomuisti on aina ulkomuisti :) Pitäisi kuitenkin olla suht hyödyllistä jos alkaa joskus värkkäillä jotain joka vaatii merkistöjen kanssa pläräämistä: ei tosiaan ole mikään yksinkertainen asia kyseessä noin kokonaisuutena ajatellen.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta