Kirjoittaja: hunajavohveli
Kirjoitettu: 30.08.2008 – 30.08.2008
Tagit: teksti, koodi näytille, vinkki
Word wrap on toimenpide, joka jakaa tekstin automaattisesti useammalle riville sen mukaan, kuinka pitkiä rivejä ruudulle kerralla mahtuu. Graafisten käyttöliittymien valmiissa komponenteissa word wrap on suorastaan itsestäänselvyys, mutta joskus on tarve koodata sopiva algoritmi itse. Tässä esitellyt word wrap -funktiot käyttävät ns. ahnetta algoritmia, joka sijoittaa riville niin paljon sanoja kuin mahdollista ja siirtyy sitten seuraavaan riviin toistaakseen saman. Optimaalisemmat algoritmit pyrkivät minimoimaan rivien loppuun jäävän tyhjän tilaan.
Toimintaperiaate on yksinkertainen: Algoritmi lukee merkkejä yksi kerrallaan pitäen kirjaa viimeksi luetun välilyönnin sijainnista. Kun merkkejä on luettu enemmän kuin riville mahtuu, viimeksi luettu välilyönti korvataan rivivaihdolla. Sana, jota oltiin lukemassa, kun rivi tuli täyteen, siirretään seuraavalle riville, ja merkkien lukemista jatketaan. Jälkimmäinen funktio poikkeaa ensimmäisestä siten, että se muodostaa jokaisesta rivistä oman merkkijononsa lisäämällä rivivaihdon sijaan lopetusmerkin ja kirjoittamalla taulukkoon jokaisen rivin alkuosoitteen. Funktiot on kirjoitettu monospace-fontille, mutta pienillä muutoksilla ne saa toimimaan myös vaihtelevan mittaisille merkeille.
// Yksinkertainen word wrap, korvaa sopivat välilyönnit rivivaihtomerkillä. // Parametreina annetaan osoitin tekstiin sekä rivin maksimipituus. Jos // teksti sisältää maksimipituutta pidemmän sanan, riviä ei katkaista. void WordWrap(char *text, int max_line_len) { int line_len = 0; // Tähän asti luetun rivin pituus int space = -1; // Viimeisin löydetty välilyönti (-1 = ei löydetty vielä) int i; // Käydään läpi tekstiä merkki kerrallaan, kunnes kohdataan lopetusmerkki. for(i = 0; text[i] != '\0'; i++) { if(text[i] == ' ') space = i; // Pidetään kirjaa viimeksi kohdatun välilyönnin sijainnista if(++line_len > max_line_len && space != -1) { // Jos luetun rivin pituus ylittää maksimin // ja välilyönti on löydetty text[space] = '\n'; // Korvataan viimeisin välilyönti rivivaihdolla. line_len = i - space; // Seuraavaa riviä on jo luettu nykyisen lukukohdan // ja katkaisukohdan erotuksen verran. space = -1; // Seuraava katkaisukohta ei vielä tiedossa } } }
// Toimii samoin kuin ylempi, mutta rivivaihdon sijaan välilyönti korvataan lopetusmerkillä. // Lisäksi annettuun lines-taulukkoon kirjoitetaan osoitin jokaisen rivin alkuun. Viimeinen // parametri kertoo, montako osoitinta voidaan enintään kirjoittaa. Paluuarvo on rivien määrä. int WordWrap2(char *text, int max_line_len, char **lines, int max_line_count) { int line_count = 1; // Rivien määrä int line_len = 0; int space = -1; int i; lines[0] = text; // Ensimmäinen rivi alkaa samasta kohdasta kuin koko teksti for(i = 0; text[i] != '\0'; i++) { if(text[i] == ' ') space = i; if(++line_len > max_line_len && space != -1) { text[space] = '\0'; // Tällä kertaa korvataan väli lopetusmerkillä line_len = i - space; if(line_count == max_line_count) break; // Jos maksimirivimäärä täynnä, lopetetaan lines[line_count] = text + space + 1; // Uusi rivi alkaa välilyönnin jälkeisestä merkistä line_count++; space = -1; } } return line_count; // Palautetaan rivimäärä }
// Esimerkkiohjelma #include <string.h> #include <stdio.h> int main(void) { // Esimerkkitekstiä char *text = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; char str[512], *lines[20]; int line_count, i; strcpy(str, text); // Kopioidaan teksti taulukkoon, jonka sisältöä voidaan muokata WordWrap(str, 64); // Wrapataan käyttäen maksimipituutena 64 merkkiä printf("%s\n", str); // Tulostetaan strcpy(str, text); // Kopioidaan teksti uudelleen line_count = WordWrap2(str, 40, lines, 7); // Tällä kertaa otetaan kaikki rivit taulukkoon for(i = 2; i < line_count; i++) { // Tulostetaan rivit 2-6 printf(" %i. %s\n", i, lines[i]); // lisäten sisennys ja numerointi } return 0; }
Loistavaa!
Yksinkertainen on kaunista.