(toiminnot)

hwechtla-tl: Tekstityökalut

Kierre.png

Mikä on WikiWiki?
nettipäiväkirja
koko wiki (etsi)
viime muutokset


Kurssin virallinen sivu on tällä hetkellä http://www.ling.helsinki.fi/kit/2008k/clt131/

Ensin kurssin hyvin lyhyt esittely, sitten tarkempi kuvaus opetuskerroista.

Kurssilla edellytettävät kieliteknolåågilliset tuotokset: frekvenssilista, kollokaatio, konkordanssi, tekstin normalisointi, sanalistat, käänteissanalistat, poiminta.

Kurssilla mahdollisesti käytäviä asioita:

Komentoja:

Opetuskerrat:

  1. yleistä raakatekstistä, tekstin normalisointi
  2. tekstin poiminta, komentojen testaus
  3. lisää säännöllisiä lausekkeita
  4. tekstin muuntaminen
  5. yksinkertaiset yhteenvedot
  6. merkistöt ja merkistömuunnokset (sitten 2 viikkoa taukoa)
  7. komentotulkin ja komentojen syntaksi, komennon ympäristö
  8. monimutkaisemmat yhteenvedot
  9. tietojen yhdistäminen useammasta lähteestä
  10. monta käyttöä sort:lle ja tr:lle
  11. skriptaus ja ohjelmointi
  12. kertaus

Materiaalia:

Yleistä raakatekstistä, tekstin normalisointi

Raakateksti on tiedostomuoto. Sen pääte on yleensä .txt, mutta Unixissa monet päätteettömät tiedostot ovat myös raakatekstiä. Raakatekstissä tiedoston sisältö tulkitaan suoraan merkeiksi, eli raakatekstitiedosto on jono merkkejä. Muotoiluja on vain muutama: rivinvaihto merkitään tietyllä merkillä ja sarkainmerkki tulkitaan vaakasuuntaiseksi hypyksi eteenpäin. Sivunvaihdollekin on merkki, mutta sitä käytetään harvoin ja harvat ohjelmat ymmärtävät sitä sivunvaihdoksi.

Mikä tahansa tiedosto on tulkittavissa raakatekstinä, koska tiedoston sisältö on oikeastaan vain jono lukuja ja jokaiselle luvulle on olemassa vastaava merkki. Monet tiedostot (kuvat jne.) ovat kuitenkin raakatekstiksi tulkittuina merkkimössöä, josta ei saa mitään selvää, ja jota ei ole toivoakaan muokata tekstitoimittimella (text editor).

Sen sijaan on myös paljon tiedostomuotoja, jotka ovat periaatteessa raakatekstiä, mutta joissa asetetaan lisää rajoitteita tiedoston sisällölle. Tällainen on esimerkiksi HTML: siinä on raakatekstin seassa tietynlaisia merkintöjä, tageja, joilla merkitään muotoilut ynnä muu, mitä ei raakatekstillä pysty esittämään. Muita esimerkkejä "rajoitetusta" raakatekstistä ovat esimerkiksi Unixin erilaiset tietokannat kuten /etc/passwd, asetustiedostot kuten /etc/pine.conf ja lokitiedostot kuten /var/log/daemon, ohjelmien lähdekooditiedostot, sekä monet kuvauskielet kuten dokumenttikuvauskieli TeX ja vektorigrafiikkakuvauskieli SVG.

Raakatekstiä onkin kahta tyyppiä, joiden välinen raja ei ole selvä. On toisaalta vapaamuotoista raakatekstiä, jossa sisältö on tarkoitettu ihmisten tulkittavaksi, toisaalta määrämuotoista raakatekstiä, jossa sisältö (tai yleensä osa siitä) on tarkoitettu ohjelmien tulkittavaksi. Tekstin, kuten tiedon yleensäkin, määrämuotoisuus helpottaa sen käsittelyä ohjelmallisesti. Esimerkki ankarasta määrämuotoisuudesta on sähköpostilistan jakelua esittävä tiedosto, jossa on yksi sähköpostiosoite jokaisella rivillä eikä mitään muuta. Lievempää määrämuotoisuutta on esimerkiksi tilauksia listaava tiedosto, jossa tilaukset on aina erotettu toisistaan tyhjällä rivillä (eli kahdella peräkkäisellä rivinvaihdolla) mutta tilausmerkinnän sisältö on jätetty tarkemmin määrittelemättä.

Täysin vapaamuotoista raakatekstiäkään ei oikeastaan ole olemassa. Kaikessa, mikä on tarkoitettu ihmisten ymmärrettäväksi, on aina jotain rakennetta, joka helpottaa ymmärtämistä. Ihmiset vain tarvitsevat määrämuotoisuutta paljon vähemmän kuin ohjelmat. Esimerkiksi suomen kielessä sanat on erotettu yleensä välilyönneillä (sekä mahdollisesti välimerkeillä), mikä tarjoaa erinomaisen keinon etsiä ohjelmallisesti kaikki sanamuodot muuten vapaamuotoisesta tekstistä. Samanlaisilla määrämuotoisuuksilla voi etsiä lauseita, virkkeitä, kappaleita, nimiä, osoitteita, puhelinnumeroita, lyhenteitä, ranskalaisilla viivoilla merkittyjä listoja ja vaikka mitä muuta.

Monet Unix-työkalut (esimerkiksi grep, sort, sed) on suunniteltu käsittelemään sillä tavoin määrämuotoista tietoa, että joka rivillä on yksi "tieto". Esimerkiksi, jos meillä on tiedosto, jossa on yksi sana per rivi, sieltä voi grepillä etsiä ne sanat, jotka "näyttävät" kaksoiskausatiiveilta. Jotkut ohjelmat (cut, sort, awk) on lisäksi suunniteltu siten, että jokainen rivi jakautuu kenttiin, jotka on eroteltu jollain merkillä, esimerkiksi välilyönnillä, kaksoispisteellä tai muulla sellaisella. Esimerkki tällaisesta tiedostosta on vaikkapa henkilötietokanta, jossa ensimmäinen kenttä on henkilön nimi, toinen sähköpostiosoite, kolmas puhelinnumero, neljäs osoite ja niin edelleen. Kullakin kentällä voi sitten olla edelleen oma määrämuotonsa.

Vapaamuotoisessa tekstissä on myös jonkin verran informaatiota, jota ei tarvita esimerkiksi sanamuotojen löytämiseen. Tällaista on välimerkkien, whitespacen eli erilaisten välilyöntien ja rivinvaihtojen, sekä isojen ja pienten kirjainten vaihtelu. Tekstin muuntamista muotoon, jossa tällaiset erot on siivottu pois ja jokainen sana on omalla rivillään, sanotaan "normalisoinniksi".

Helpoin normalisointi lienee:

tr -sc "[:alnum:]-_:'" '\n' | tr '[:upper:]' '[:lower:]'

Esimerkkejä raakatekstistä:

Raakatekstiä on siis todella monenlaista ja Unixin tekstityökalut soveltuvat kaiken raakatekstin käsittelyyn ainakin jossain määrin. Myös ei-raakatekstille (esimerkiksi Word-dokumentit) on olemassa työkaluja, jotka heittävät pois kaiken muotoilun ja antavat sisällön raakatekstinä, esimerkiksi catdoc ja antiword.

Yksinkertainen tekstin muokkaus

Erot kahden tiedoston välillä:

diff -u /etc/motd motd

Replace (single character only. For replacing multiple characters use sed):

cat motd | tr a b
tr a b < motd

cat motd | tr Aa Bb
cat motd | tr ie ei
cat motd | tr aeiou i

All lower case:

cat motd | tr A-Z a-z
cat motd | tr '[:upper:]' '[:lower:]'

Sanat omille riveilleen:

cat motd | tr ' ' '\n'

Squeeze repeats: Words on separate lines and remove empty lines

cat motd | tr -s ' ' '\n'

Delete characters:

cat motd | tr -d ' '
cat motd | tr -d aeiou

Merkkiluokan käänteisluokka (kaikki paitsi vokaalit tuhotaan, jättää siis vain vokaalit):

cat motd | tr -d -c aeiou

Produce list of words in text:

cat motd | tr -s -c '[:alpha:]' '\n'
cat motd | tr -sc "[:alnum:]-_:'" '\n' | tr '[:upper:]' '[:lower:]'

Tekstin poiminta

grep tyhj | head
grep täysi tiedosto; grep täyde tiedosto; grep täyte tiedosto
egrep täy(si|[td]e)

Ongelma: entä jos mahdollisia haettavia on ääretön määrä? Ääretöntä joukkoa erilaisia merkkijonoja ei voi luetella... miten se merkitään?

egrep '<[^>]*>' | less
egrep '[[:upper:]][[:lower:]]+'
egrep '[[:alnum:]-_%+]+(\.[[:alnum:]-_%+]+)*@[[:alnum:]-.]*\.[a-zA-Z]+'
egrep '[h]ttp://[^ ]*[[:alnum:]/=]'

cat Kalevala | norm | grep '[aeiouyäö]vi$'
cat Kalevala | norm | grep 'ikse$'
cat Kalevala | norm | grep 'ihe$'

egrep '([^dfghjklmnprstv]*[aeiouyäö]+){5}'

Säännöllisistä lausekkeista:

man regex
grep - traditional
egrep - extended

grep - Global Regular Expression Print toimii riveittäin, näyttää rivit joilla osuma

esim. sana ovi:

grep '\<ovi\>' tiedosto
grep 'ovi\>' tiedosto

\< ja \>: täsmäävät sanan alkuun ja loppuun, sanan alku = ei-sanakonstituentti merkki jonka perässä sanakonstituentti merkki ja loppu päin vastoin.

egrep - Extended grep

egrep '\<ov(i|e)' Kalevala
egrep '\<ov(ia|ea|en)' Kalevala

egrep -o, --only-matching: Show only the part of a matching line that matches PATTERN. Jos rivillä on useampia osumia, näyttää ne kaikki omilla riveillään. Ei silti toimi rivien yli, ts. etsittävä asia ei voi jatkua useammalla rivillä.

Harjoitus:

cat Kalevala | tr -sc "[:alnum:]-'" '\n' | head
cat Kalevala | tr -sc "[:alnum:]-'" '\n' | egrep vi
cat Kalevala | tr -sc "[:alnum:]-'" '\n' | egrep 'vi\>'
cat Kalevala | tr -sc "[:alnum:]-'" '\n' | egrep 'ihe\>'
cat Kalevala | tr -sc "[:alnum:]-'" '\n' | egrep 'ihe$' | wc -l
cat Kalevala | tr -sc "[:alnum:]-'" '\n' | egrep 'täy(si|de|te)(en|lle)'

säännöllisten lausekkeiden maagiset merkit

. - mikä tahansa merkki
\< - sanan alku
\> - sanan loppu
\ - escape character
^ - rivin alku
$ - rivin loppu
(...|...|...) - vaihtoehtoja

Yksinkertaiset yhteenvedot

Display all words in text:

cat /l/kielipankki/parole-fi/latest/a-lic/kirjat-a/* |
tr -sc "[:alnum:]" '\n' | tr '[:upper:]' '[:lower:]' | grep valehtelij

Display all word forms in text:

cat /l/kielipankki/parole-fi/latest/a-lic/kirjat-a/* |
tr -sc "[:alnum:]" '\n' | tr '[:upper:]' '[:lower:]' | grep valehtelij |
sort -u

Count all occurrences:

cat /l/kielipankki/parole-fi/latest/a-lic/kirjat-a/* |
tr -sc "[:alnum:]" '\n' | tr '[:upper:]' '[:lower:]' | grep valehtelij |
sort -u | wc -l

Display all word forms with count:

cat /l/kielipankki/parole-fi/latest/a-lic/kirjat-a/* |
tr -sc "[:alnum:]" '\n' | tr '[:upper:]' '[:lower:]' | grep valehtelij |
sort | uniq -c | sort -rn

Find most common word form

cat /l/kielipankki/parole-fi/latest/a-lic/kirjat-a/* |
tr -sc "[:alnum:]" '\n' | tr '[:upper:]' '[:lower:]' | grep valehtelij |
sort | uniq -c | sort -rn | head -1 | cut -c9-

Each character on separate line:

sed -r 's/./&\n/g' tiedosto
egrep -o . tiedosto

make Bigrams:

tail +2 sanat.txt | paste sanat.txt -

or:

cat test.txt | sed -e x -e G -e '2,$s/^[^\n]*\n//' -e h -e 's/\n/ /g'
cat test.txt | sed -e x -e G -e '3,$s/^[^\n]*\n//' -e h -e 's/\n/ /g'

Bigrams of two letters:

sed -r 's/./&\n/g' test.txt | sed -e x -e G -e '2,$s/^[^\n]*\n//' -e h -e 's/\n/ /g'

Kielessä on sanoja, joilla on sananmuotoja, joilla on sitten teksteissä esiintymiä. Sananmuotojen esiintymiä sanotaan saneiksi. Saman sananmuodon eri esiintymät ovat samoja merkkijonoja keskenään. Tekstin normalisointi tuottaa listan tekstin saneista esiintymisjärjestyksessä.

wc -l
sort [-f] | uniq -c[i] | sort -rn
sort -u[f] | wc -l
sort [-f] | uniq -c[i] | sort -rn | head -1 | cut -c9-

Rivien n-grammit saadaan putkessa näin (korvaa <n> sopivalla luvulla):

sed -e x -e G -e '<n>,$s/^[^\n]*\n//' -e h -e 's/\n/ /g'

Perinteisempi tapa on tehdä valmiista tiedostosta bigrammit:

tail +2 tiedosto | paste tiedosto -

Säännöllisiä lausekkeita

vowels, number, small letters, small letters + ":"

(a|e|i|o|u|y|ä|ö|å) = [aeiouyäöå]
[0-9]
[[:lower:]]
[[:lower:]:]

repetition:

* - 0-infinite times
+ - 1-infinite times

a* - "", "a", "aa", "aaa"

ba*c - "bc", "bac", "baac", "baaac", ...

(ba)*c - "c", "bac", "babac", "bababac", ...

(hi|ho)+ = (hi|ho), (hi|ho)(hi|ho), (hi|ho)(hi|ho)(hi|ho), ...

all words ending in ":":

[[:alpha:]'-]+:

Kaikki sanat a...i

egrep -o '\<a[[:alpha:]]*i\>'

.* - kaikki merkkijonot, myös tyhjä

Käänteismerkkiluokat ^

Vokaalit; kaikki paitsi vokaalit; mikä tahansa merkkijono, johon ei sisälly välilyöntiä:

[aeiouyäö]
[^aeiouyäö]
[^ ]*

Esim: Kaikki sanat, jossa valehtelij ja seuraava sana:

egrep -o 'valehtelij[^ ]* [^ ]+'

kaikki nettiosoitteet:

zcat /l/kielipankki/sfnet/raw_texts/harrastus/sfnet.harrastus.mensa.gz |
egrep -o 'http://[^ ]*'

Only matching

egrep -o '[[:alpha:]]+-[[:alpha:]]+' Kalevala
egrep -o '[[:upper:]][[:upper:]]+' /l/kielipankki/misc/Kalevala
egrep -o '\<[[:upper:]]+\>' /l/kielipankki/misc/Kalevala

Lisää säännöllisistä lausekkeista

echo 'epäonnistuu (tällaisella) (syötteellä)' | egrep -o '\(.*\)'
echo 'toinen (tapa (epäonnistua) on tämä)' | egrep -o '\([^)]*\)'
echo 'entä (mitä (seuraa) tästä?) (hmm?)' | egrep -o '\([^()]*\)'

Miten kummassa voisi löytää sulkeet, jotka saavat sisältää toisia sulkeita?

echo 'yksi (koe (vielä) lisää) kiitos' | egrep -o '\(([^()]|\([^()]*\))*\)'
echo 'mutta (ei (toimi (näin)) ...)' | egrep -o '\(([^()]|\([^()]*\))*\)'

Sulkeet, jotka sisältävät sulkeita mielivaltaisen syvälle, kuuluvat kontekstivapaiden kielten luokkaan. Koska säännöllisillä lausekkeilla pystyy kuvaamaan vain säännöllisiä kieliä, ei ole olemassa säännöllistä lauseketta, jolla voisi tunnistaa tällaisen mielivaltaisen syvän suljelausekkeen.

Intuitiivisesti ajatellen ongelma on siinä, että mielivaltaisesti upotettujen sulkeiden tunnistamisessa pitäisi pitää muistissa, miten monien sulkeiden sisässä ollaan. Koska säännöllisen lausekkeen tunnistavalla automaatilla on äärellinen määrä tiloja, se ei voi merkitä muistiin mielivaltaisen monten sulkeitten sisässä olemista.

Kontekstivapaisiin kieliin törmää vähän väliä. Niitä ovat kaikki kielet, jossa jokin elementti (esimerkiksi lause) voi sisältää samanlaisia elementtejä (siis sivulauseita). Luonnollisista kielistä väitetään, että ne ovat vähintään kontekstivapaita, mutta itse asiassa ihmiset eivät pysty ymmärtämään mielivaltaista määrää upotuksia. Sen sijaan monet ohjelmointikielet ovat aitoja kontekstivapaita kieliä, samoin merkintäkielet: esimerkiksi HTML-kielessä listat sisältävät listakohtia, jotka voivat taas sisältää toisia listoja.

Säännöllisillä lausekkeilla pystyy emuloimaan kontekstivapaata kieltä tiettyyn syvyyteen asti yllä näytetyllä tavalla.

Tekstin muuntaminen

tr -s '\n'
cat -s
tr -sc 'aeiouyåäö\n' C
tr 'abcdefghijklmnopqrstuvwxyzåäö' 'VCCCVCCCVCCCCCVCCCCCVCCCVCVVV'
sed -r 's/^[[:space:]]*$/-PARA-/' | tr -s '[:space:]' '\n' | \
        sed -r 's/-PARA-//'
sed -r 's/<[^>]*>//g'
sed -r 's/<p( [^>]*)?>/\n\n/g'
sed -r 's/(.)(.)/\2\1/g'

tr -sc "[:alnum:]-_:'" '\n' | tr '[:upper:]' '[:lower:]' | \
        cut -c1 | sort | uniq -c
last | cut -c0-9,51-55 | sed -r 's/^([a-z]+) *(.*)/\2 \1
ps uax | grep 'sh$' | cut -c0-9,64-
cut -d: -f1,5 /etc/passwd | tr : ' '
cut -d: -f5 /etc/passwd | cut -d, -f2 | tr -d ' ' | sort | uniq -c

replace with sed -r (stream editor): s - substitute g - global

replace (one or more) t, p and k with BÖÖ

zcat /l/kielipankki/sfnet/raw_texts/harrastus/sfnet.harrastus.mensa.gz |
sed -r 's/[tpk]+/BÖÖ/g' | less

replace all aa with a:

zcat /l/kielipankki/sfnet/raw_texts/harrastus/sfnet.harrastus.mensa.gz |
sed -r 's/aa/a/g' | less

strip tags:

cat /l/kielipankki/parole-fi/latest/a-lic/kirjat-a/k-Turkka.sgml |
sed -r 's/<[^>]+>//g'

Komennon ympäristö

Merkistöt ja merkistömuunnokset

Tekstitiedostot (ja muutkin tiedostot) ovat vain sarja tavuja. Merkistöt ovat puolestaan tavujen sisältämien numeroiden tulkintasopimuksia. Lisää aiheesta esim. Wikipediassa: http://fi.wikipedia.org/wiki/Merkist%C3%B6


Heikon muistiinpanot luennolta:

Tekstitiedostot koostuvat numeroista 0-255, esim 65=A.
Merkistö on tulkinta numeroista merkiksi.

Nykyään käytännössä kaikki merkistöt pohjautuvat ASCII-merkistöön (harvat EBCDIC-merkistöön).

Latin-merkistöt laajennettu ASCII.
Käytännössä yleisin meillä nyt on Latin-1 (ISO-8859-1) ja Latin-9 (ISO-8859-15).

Unicode
yleisin UTF-8
unicode määrittää jokaiselle merkille luvun.
koodaus määrittää, miten nämä luvut esitetään tiedostossa.

Näytä merkin tiedot
echo ä | od -x

ä
Latin-1: 228
UTF-8: 195 164 (Latin-1 tulkittuna jotain Ã)
esim. echo ä | iconv -f latin1 -t utf8

ASCII-teksti toimii sellaisenaan Latin-1 ja UTF-8 tekstinä.

merkistömuunnoksia tehdään komennolla iconv
echo pää | iconv -f latin1 -t utf8

virheelliset merkit poistetaan: 'iconv -c'
echo pÃÃ pää | iconv -c -f utf8 -t latin1
echo pää | iconv -c -f latin1 -t ascii

näytä tunnetut merkistöt:
iconv -l

Yhteenveto:
ASCII: vain englannin merkkejä, ei ääkkösiä yms., 0-127
Latin-*: ASCII 0-127 + paikallisia lisäyksiä 128-255
UTF-8: Kaikki maailman merkit. Laajentaa ASCII-merkistöä. Ei-ASCII-merkit esitetään lukusarjoina 128-255.


Pikalinkit:


kommentoi (viimeksi muutettu 08.04.2008 18:33)