Rivinvaihdot ja kappaleet datan käsittelyssä, luku 2 Rivinvaihtojen esitystapoja:

Ohjelmointikielet

Rivinvaihdot lähdekielisissä ohjelmissa

Eräät ohjelmointikielet, kuten Fortranin vanhimmat versiot, ovat vahvasti rivirakenteisia siinä mielessä, että lähdekielisen ohjelman (source program) pitää jakautua riveille määrätyllä tavalla. Muissakin kielissä on yleisesti sääntöjä, jotka rajoittavat ohjelman vapaata jakamista eri riveille. Esimerkiksi C-kielessä on ns. direktiivit kirjoitettava kukin omalle rivilleen.

Erittäin tavallista on, että merkkijonovakiota ei saa ainakaan suoraan jakaa eri riveille. Koska toisaalta usein tarvitaan pitkiä merkkijonovakioita, on yleensä käytössä jokin jatkorivikonventio. Tyypillinen konventio on sellainen, että rivin loppuun kirjoitetaan kenoviiva \ ja kirjoittamista jatketaan heti seuraavan rivin alusta (vaikka muuten olisi käytetty sisennyksiä). Esimerkki C-kielestä:

char *s = "Tämä\
on merkkijono"
merkitsee samaa kuin
char *s = "Tämäon merkkijono"

Huomannet sudenkuopan: on helppoa luulla, että rivinvaihto vastaisi välilyöntiä, kuten se usein vastaa. Esimerkiksi Fortranin uusissa versioissa (Fortran-90:stä alkaen) ns. vapaassa muodossa (free source form) on suunnilleen vastaava jatkorivikonventio mutta merkkinä on \:n asemesta et-merkki &; tosin asiaan vielä liittyy eräitä lisäsääntöjä.

Rivinvaihtojen sisällyttäminen merkkijonovakioon saattaa myös olla mahdollista. Tämä on tarkoin erotettava edellä kuvatusta jatkorivikäytännöstä. C-kielessä, ja sitä matkien useissa muissa kielissä, voidaan tarkoitukseen käyttää merkintää \n. Esimerkiksi "hui\nhai" on merkkijonovakio, jonka sisällä on rivinvaihto. C-standardin mukaan \n on kontrollikoodi, jonka merkitys on "moves the active position to the initial position of the next line". (Tästä seuraa, että sen fyysinen toteutus riippuu siitä järjestelmästä, jossa C-ohjelma suoritetaan. Kuitenkin sen tulee olla yksi koodi, yksi "ohjausmerkki", esim. LF tai CR mutta ei niiden yhdistelmä, koska \n vie (esim. merkkijonovakion sisäisessä esityksessä) saman verran tilaa kuin yksi merkki. Tästä seuraa, että jos järjestelmässä on käytössä CR LF rivinvaihdon esityksenä, täytyy C-kielen toteutuksen automaattisesti muuntaa \n:n sisäinen esitys tulostuksessa CR LF:ksi.)

Rivinvaihdot syötössä ja tulostuksessa

Aivan eri asia sitten on, miten ohjelma lukee tekstidataa ja kirjoittaa sitä. Kuten kohdassa Rivi ja tietue mainittiin, aluksi käsittely oli yleensä rivipohjaista - ja on usein nykyisinkin. Vaikka esimerkiksi Perliä pidetään modernina kielenä, se on suunniteltu ensisijaisesti rivi kerrallaan tapahtuvaan datan käsittelyyn. Perlin yksinkertaisimman käytön perusidiomeihin kuuluu rakenne

while(<>) {
    käsittele syöterivi
    print; }

missä käsittely voi koostua esimerkiksi käskystä s/x/ks/g; (korvaa jokainen x-kirjain kirjainparilla ks). Tässä ei missään vaiheessa erikseen sanota, mikä data on käsittelyn kohteena, vaan se on koko ajan implisiittisesti "nykyinen syöterivi". (Tosin siihen voitaisiin viitata muuttujalla $_.)

Useat ohjelmointikielet on suunniteltu niin, ettei tekstidataa edes voi lukea ja tulostaa kuin rivi kerrallaan. Toki yksittäisiä merkkejäkin voi käsitellä, mutta siten, että esim. syötteen käsittelyssä ohjelma ensin lukee rivin vaikkapa taulukkoon, josta se sitten indeksoimalla poimii merkkejä. Tämä toimi ja toimii hyvin monissa tilanteissa, mutta vuorovaikutteisista ohjelmista tulee kömpelöitä, jos jokainen näppäimistöltä annettava ohjaus on päätettävä return- tai enter-näppäintä painamalla. Muutoinkin vuorovaikutteinen, merkki kerrallaan tapahtuva syöttö ja tulostus vaativat usein erikoistoimia, esimerkiksi erityisesti siihen sopivien aliohjelmien käyttöä.

Erikseen mainittakoon Fortran-ohjelmoinnin se vaikeus, että jokainen write- tai print-lause tulostaa vähintään yhden rivin. Aiemmin tämä ongelma kierrettiin järjestämällä tulostustoiminnot niin, että ohjelma tulostaa vain tilanteissa, joissa sillä on koko rivi "valmiina". Tämä ei tietenkään ollut mahdollista esim. tilanteessa, jossa halutaan tulostaa tekstiä kehotteeksi niin, että käyttäjä voi kirjoittaa syöttötiedot suoraan sen perään, samalle riville. Saatettiin myös käyttää erilaisia kielen epästandardeja laajennuksia. Fortran-90:ssä ongelma ratkeaa siististi käyttämällä tulostuslauseessa argumenttia advance='no'.

Toisaalta silloinkin, kun kielen syöttö- ja tulostustoiminnot on suunniteltu ensisijassa merkeittäiseen käsittelyyn, kannattaa usein käyttää rivipohjaista käsittelyä apuna. Esimerkiksi C-kielisen ohjelmoinnin tyylioppaat usein suosittavat tekemään ohjelmat niin, että ohjelma ensin lukee rivin merkkejä puskuriin (merkkitaulukkoon) ja sitten siitä sitten esimerkiksi sscanf-funktiolla (eikä suoraan syötetiedoista scanf-funktiolla). Jos esimerkiksi on olennaista tunnistaa, miten numeerinen syöttötietoaineisto jakautuu eri riveille, niin scanf-funktiota ei voi käyttää, koska se iloisesti ohittaa rivinvaihdot pitäen niitä välilyönteihin rinnastuvina.

Yleensä ns. korkean tason ohjelmointikielet on pyritty suunnittelemaan niin, että ohjelmat voidaan kirjoittaa riippumattomiksi rivinvaihtojen esityksestä. Ohjelman voi siis koodata lukemaan rivi kerrallaan tai tunnistamaan rivinvaihtoja ilman, että ohjelmoijan tarvitsee edes tietää, onko rivinvaihdon esitysmuoto CR, LF, CR LF vai kenties jotain muuta. Esimerkiksi kun C-ohjelma lukee tiedostosta dataa merkki kerrallaan, se "näkee" rivinvaihdon C-standardin mukaisella tavalla kontrollikoodina, johon voidaan viitata merkinnällä \n (esim. if(merkki=='\n')...). Näin siitä riippumatta, mikä rivinvaihtojen fyysinen esitysmuoto tiedostoformaatissa on. Eri asia sitten on, että lukemalla tiedostoa binaarisesti (esim. fread-funktiolla) on mahdollista päästä näkemään se fyysinen esitysmuotokin.

Selvää on, että ohjelmoijan kannattaa tulostuksessa vastaavasti yleensä käyttää kielen rakenteita, jotka on määritelty niin, että ne saavat aikaan rivinvaihdon, esim. Pascalissa writeln ja C:ssä vaikkapa putchar('\n'), yhden monista mahdollisuuksista mainitaksemme. Sen sijaan putchar('\012') tarkoittaisi LF-koodia (012 on sen luvun oktaaliesitys, jonka desimaaliesitys on 10). Vaikka vaikutus on useissa järjestelmissä sama, niin se ei suinkaan ole yleisesti sama.

Yleensä tulostuksen loppuunkin on syytä kirjoittaa rivinvaihto eli kirjoittaa viimeinen rivi kuten muutkin, esim. C:ssä printf("Loppu.\n"), ei printf("Loppu."). Perusteluja tälle on seuraavassa kohdassa.