Split en sætning

Sproget er mudret og besværligt. Og selvom det umiddelbart lyder som en enkel opgave, så kræver det alligevel en del krumspring at dele en dansksproget tekst op i sætninger.

De fleste programmer laver det, jeg vil kalde et naivt split. Vi finder nogle symboler i sproget, der normalt opdeler en tekst i de enkelte sætninger (perioder). Det fungerer langt hen ad vejen glimrende.

I den første version https://tekstr.dk/app/ brugte jeg også en rimelig naiv funktion til at dele sætninger op. Den så sådan her ud:

const sentences = function (text) {
var sentenceArray = [];
var sentences = text
.replace(/\. ([a-z])/g, ' $1')
.replace(/\.([a-z])/g, '$1')
.split(/\?|\!|\. |\." |\n/g)
.forEach(function (element) {
sentenceArray.push(element.trim());
});

return sentenceArray.filter(Boolean);
};

Baggrund

Fodrer vi den med en almindelig sætninger får vi følgende:

const string = 'Jeg skal plukke kokosnødder. Der er ingen kokosnødder! Har du også plukket kokosnødder? Eller har en anden plukket alle kokosnødderne!';

sentences(string); // ["Jeg skal plukke kokosnødder", "Der er ingen kokosnødder", "Har du også plukket kokosnødder", "Eller har en anden plukket alle kokosnødderne"]

Det ser jo rigtigt nok ud.

Jeg har sågar indsat et par replaces i starten, der på snedig vis fanger en lille håndfuld danske forkortelser.

Men den fanger langt fra alle. Skriver du Hr. Smith, så tæller den det som to selvstændige sætninger. Punktum er en logisk måde at opdele sætninger, lige indtil mennesker begynder at bruge forkortelser.

Samtidig er min sentences() ret krævende, når det kommer til den tekst, du fodrer den med. Det kan jeg tillade mig, fordi jeg kender input meget godt fra teksteditoren i det her tilfælde.

Men drømmen er at skrive en række funktioner, der kan bruges vidt og bredt, hvorend nogen kunne finde på at arbejde med det danske sprog i en browser. Derfor skal den også kunne håndtere flere forskellige former for tekstinput. Måske endda ren HTML.

Mere lavpraktisk, så splitter den også lige nu på en måde, hvor den fjerner de tegn, den splitter på. Så der kun står selve sætningen tilbage. Det var okay, da vi skulle markere tekst i en simpel editor. Det er lidt mere problematisk, når vi skal arbejde os frem mod en POS-tagger, hvor parenteser og spørgsmålstegn er betydningsbærende og derfor skal bevares. Før talte vi bare sætningerne, nu skal vi arbejde os frem mod at kunne forstå lidt mere om dem.

Der er altså tre grundlæggende problemer, der skal løses:

  1. Større præcision i opdeling af sætninger.
  2. Mere fleksibilitet i forhold til det input, vi kan fodre den med.

Præcision er min klare førsteprioritet.

Tests

Jeg konstruerer selv en stribe tekststrenge af varierende længde, som jeg skal bruge til at teste med i første omgang. Det kan automatiseres, fordi jeg ved præcis hvilket resultat jeg vil have af min funktion.

Udfordringen er at finde en masse skøre sproglige cases, som vi skal tage højde for i funktionen.

testOne =
'O\'malleys Bar (det herlige sted), er tæt på en forfærdelig konkurs. Jeg er i panik. Hvor skal jeg nu drikke min ugentlige dram med Hr. Henrik Jensen? Han var også desperat. Han sagde: "Hvad skal det hele dog nytte. Jeg har 4.000 kr., jeg skal have drukket op inden marts." Jeg er bange for, at festen er slut før 4:30 i morgen tidlig.';

resultOne = [
"O'malleys Bar (det herlige sted), er tæt på en forfærdelig konkurs.",
'Jeg er i panik.',
'Hvor skal jeg nu drikke min ugentlige dram med Hr. Henrik Jensen?',
'Han sagde: "Hvad skal det hele dog nytte. Jeg har 4.000 kr., jeg skal have drukket op inden marts."',
'Jeg er bange for, at festen er slut før 4:30 i morgen tidlig.',
];

Potentielle løsninger

Det er selvfølgelig altid sjovt selv at skrive et præcist stykke kode.

Men for fremdriftens skyld har jeg kigget på om der findes gode alternativer, der må bruges kommercielt og samtidig nemt kan tilpasses dansk.

Her er SBD - Sentence Boundary Detection et interessant bud: https://github.com/Tessmore/sbd