Join Now

Juletip #3 - SAS Regular Expressions

by SAS Employee ChristianKjeldsen on ‎12-03-2014 05:58 AM (581 Views)

SAS Regular Expressions

Kender du til SAS Regular Expressions? Hvis ikke så kan det være at dette juletip er noget for dig. SAS Regular Expressions er baseret på Perl Regular Expressions, hvilket er grunden til, at de respektive SAS funktioner starter med PRX.

SAS Regular Expressions benyttes til at danne en mønster for en tekststreng, man enten gerne vil finde eller erstatte. Typiske brugstilfælde for dette er:

  • Se om en tekst indeholder bestemte strenge
  • Tjekke om en tekst overholder et bestemt format
  • Udtage enkelt dele af en tekststreng
  • Erstatte tekst med noget andet


Der kommer eksempler på dette senere i dette juletip.


For at danne mønstrene benyttes tegn og metakarakterer. Det er brugen af disse metakarakterer, der gør Regular Expressions til et meget stærkt værktøj – men som også kan gøre Regular Expressions svære at læse og forstå.


En tabel med de mest brugte regular expression metakarakterer:

Tegn

Betydning

Kvantificeringer

*

0 eller flere (af tegnet eller gruppen foran)

+

1 eller flere forekomster

?

0 eller 1 forekomster

{m}

Præcis m forekomster

{m,n}

Mellem m og n forekomster

Karakterklasser

\s

White space (mellemrum, tabulator, osv.)

\w

Alle bogstaver fra A-Z, både store og små, tallene 0-9 begge inklusive samt underscore 

\d

Tallene fra 0-9 begge inklusive

.

Alle tegn – bogstaver, tal, tegn, whitespace (dog newline undtaget)

Ranges

[]

Tegn skrevet mellem klammer, skal forstås som ”tegn der må stå på denne plads” – lin[ij]e vil matche både linie og linje

()

Alt der står mellem parenteser vil blive ”husket”, og kan refereres til med $n.

I SAS er der implementeret flere funktioner til regular expressions. 4 af de mest brugte funktioner er givet her:

Funktion

Betydning

PRXPARSE

Danner et id for en regular expression

PRXMATCH

Tester om en tekst-streng matcher den givne regular expression

PRXPOSN

Kan hente ”huskede” strenge

PRXCHANGE

Erstatter en matchet tekst-streng med en ny streng

Efterfølgende vil jeg vise nogle eksempler med disse funktioner. Alle eksempler baseres på følgende datasæt:


data Karakterer;

input linje $ 1-80 ;

datalines;

Andersen, Anne: Matematik 12, Dansk 7

Boelt,  Bjarne B.: Matematik10, Dansk   4, Økonomi 4

Gormsen, Gert: Dansk 12

Nissen, Naja: Dansk

;

run;

For at angive en Regular Expression laves en SAS karaktervariabel, som starter og slutter med /. Det vil sige en regular expression ser ud som: "/<regular_expression>/"

I første eksempel vil jeg blot se, om teksten ”Matematik” indgår i en række. Hvis den gør det, kommer rækken med i output.

/* Eksempel på Regular Expression, der finder rækker med Matematik */

data Matematik_række;

     set Karakterer;

     if prxmatch("/Matematik/", linje);

run;

Her benyttes SAS funktionen prxmatch, og en regular expression, der blot er den tekst man søger på.

Når dette køres i SAS fås følgende resultat, hvor det ses at kun Anne og Bjarne er med som forventet.

SAS_Juletip_2014_eks_1.PNG

I andet eksempel vil vi nu finde rækker, som følger et bestemt mønster. Det vi forventer er at linjen er på denne form:

Efternavn, Fornavn: Fag Karakter

/* Eksempel på Regular Expression, der finder rækker med karakterer, og hvor der både er komma og kolon */

data Karakter_række;

     set Karakterer;

     if prxmatch("/\w+,.+:\s+\w+\s+\d+/", linje);

run;

Dette ser jo straks meget mere kompliceret ud. Hvis vi tager hvert tegn og metakarakter for sig, så betyder de:

\w+       Et eller flere alfanumeriske tegn før første komma (efternavn)

,           Et komma

.+         Alle tegn efter første komma og inden kolon(fornavn og mellemnavn inklusiv punktum)

:           Et kolon

\s+       Et eller flere mellemrum efter kolon

\w+       Et eller flere alfanumeriske tegn efter kolon (første fag)

\s+       Et eller flere mellemrum mellem første fag og første karakter

\d+       Et eller flere ciffre (første karakter)

Resultatet af dette program bliver

SAS_Juletip_2014_eks_2.PNG

Undrer du dig nu over, hvorfor hverken Bjarne eller Naja er med? Naja er ikke med, da hun slet ikke har fået en karakter. Bjarne ikke er med, fordi der mangler et mellemrum mellem faget matematik og karakteren.

Hvis regular expression blev ændret en smule, så den ikke krævede et mellemrum mellem faget og karakteren kunne den se ud som (et + er erstattet med *):


data Karakter_række_2;

     set Karakterer;

     if prxmatch("/\w+,.+:\s+\w+\s*\d+/", linje);

run;

Hvilket som forventet giver dette resultat

SAS_Juletip_2014_eks_2_2.PNG

I tredje eksempel vil vi gerne have matematik karakteren ud. Her bruger jeg 3 PRX funktioner. Til at starte med benytter jeg PRXPARSE til at danne en Regular Expression, der kan benyttes af de andre funktioner. Dette gør jeg af to grunde: for det første behøver jeg ikke, at skrive den flere gange (og ja, man ændrer ofte sin regular expression i starten indtil den faktisk finder det man ønsker….), for det andet tillader PRXPOSN ikke at man skriver sin regular expression direkte i funktionen, men den skal være parset først.

Når først min regular expression er oprettet benytter jeg først PRXMATCH til at sikre, at der faktisk er en matematik karakter, for endelig at bruge PRXPOSN til at hente den ud.

Mit program ser derfor ud som:


/* Eksempel på Regular Expression, der kun beholder matematik karakterer */

data Matematik_karakterer;

     set Karakterer;

     retain re_id;

     if _N_=1 then

           re_id=prxparse("/Matematik\s*(\d+)/");

     if prxmatch(re_id, linje);

     Matematik=prxposn(re_id, 1, linje);

     drop re_id linje;

run;

Grunden til, at man kan hente data ud, er, at der er benyttet et sæt parenteser. Dette gør at den tekst, der matches med inde i parenteserne, gemmes i en buffer, som kan hentes ud via PRXPOSN. Tallet 1 angivet i denne funktion viser, at det er indholdet af første buffer man ønsker. Hvis man havde flere sæt parenteser ville de respektive tekster blive gemt i fortløbende buffere. Men for igen at tage denne regular expression og nedbryde i sine enkelt dele er det:

Matematik         Teksten Matematik med stort begyndelsesbogstav

\s*        Ingen eller flere mellemrum efter teksten Matematik

(\d+)     Et eller flere ciffre som gemmes i buffer (Matematik karakter)

Resultatet her bliver matematik karaktererne for Anne og Bjarne:

SAS_Juletip_2014_eks_3.PNG

Mit sidste eksempel viser hvordan man kan benytte PRXCHANGE til direkte at søge og erstatte i en tekst. Når først man har indset metodikken her, vil man opdage, at dette er en rigtig smart måde at skrive dette på. For at søge og erstatte i en tekst skal ens regular expression skrives på en lidt anden måde. Formen er:

"s/<regular_expression_på_søgetekst>/<tekst_der_skal_erstattes_med>/"

Første s angiver, at det er søg-og-erstat. Mellem de to første / findes den regular expression man søger efter, mens der mellem anden og tredje / angives den tekst, der skal erstattes med.

I dette eksempel benytter jeg PRXCHANGE to gange. Første gang byttes rundt på fornavn og efternavn. Anden gang forkortes alle fag til kun at have de tre første bogstaver.

For at gøre dette lidt mere overskueligt bruger jeg kun Bjarne som eksempel, og viser hvordan linjen er blevet ændret efter hver brug.

/* Eksempel på Regular Expression, der erstatter tekststrenge */

data Karakterer_pænt;

     set Karakterer;

     if _N_=2; * Bruger Bjarne som eksempel;

     output;

     * Byt rundt på fornavn og efternavn, samt indsætte lidt ekstra tekst;

     linje=prxchange("s/(\w+),\s*(.+):/$2 $1: /", -1, linje);

     output;

     * Forkort fag og indsæt 1 mellemrum mellem fag og karakter;

     linje=prxchange("s/(\w{3})[A-Za-zÆØÅæøå]*\s*(\d+)/$1. $2/", -1, linje);

     output;

run;

Man ser, at der står -1 som andet argument til PRXCHANGE. Dette betyder, at alle forekomster skal erstattes.

Første regular expression, der søges efter, indeholder følgende: 

(\w+)     Et eller flere alfanumeriske tegn inden komma, som gemmes i buffer 1 (efternavn)

,           Et komma

\s*        Ingen eller flere mellemrum før fornavn (disse vil vi ikke have med)

(.+)       Alle tegn efter første komma og mellemrum og inden kolon, som gemmes i buffer 2

(fornavn og mellemnavn inklusiv punktum)

:           Et kolon

Den tekst streng der skal erstattes med indeholder

$2         Fornavn og mellemnavn gemt i buffer 2

            Et mellemrum

$1         Efternavn gemt i buffer 1

:           Et kolon

Anden regular expression, der søges efter, ser måske lidt tung ud. Den kvikke læser vil måske spørge sig selv, hvorfor jeg ikke bare bruge \w i stedet for [A-Za-zÆØÅæøå]. Men da \w matcher på alfanumeriske tegn, og derfor også tal, vil teksten Matematik10 faktisk blive opdelt som faget Matematik1 med karakteren 0, i stedet for faget Matematik med karakteren 10, som ønsket. Men for igen at dele denne regular expression op er den som følger:


(\w{3})               Tre bogstaver, lige meget om de er store eller små gemt i buffer 1

(Tre første bogstaver i faget)

[A-Za-zÆØÅæøå] Alle bogstaver efter de tre første (Resten af navnet på faget)

\s*                    Ingen eller flere mellemrum efter faget

(\d+)                 Et eller flere ciffre gemt i buffer 2 (karakteren)

Den tekst streng der skal erstattes med indeholder

$1         Tre første bogstaver i faget gemt i buffer 1

.           Et punktum

            Et mellemrum

$2         Karakteren gemt i buffer 2

Resultatet kommer derfor til at se ud som:

SAS_Juletip_2014_eks_4.PNG

Jeg håber, at dette meget lange juletip gav en god introduktion til SAS regular expressions. For mere information kan man se her:

http://support.sas.com/documentation/cdl/en/lefunctionsref/67398/HTML/default/viewer.htm#n13as9vjfj7...

http://support.sas.com/documentation/cdl/en/lefunctionsref/67398/HTML/default/viewer.htm#p0s9ilagexm...

Attachment
Comments
by Senior User DaisyKyed
on ‎12-04-2014 04:44 AM

Jeg kan også kun tilslutte mig at Regular Expressions er en god løsning i mange situationer. Jeg stiftede første gang bekendtskab med det i dine UDEVA-programmer og har siden brugt det i mine egne. Derfor er det et godt initiativ at få viden om det udbredt til flere.