Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

XSLT/XPath: Hente ut deler av en streng

5 views
Skip to first unread message

Eira Monstad

unread,
Oct 18, 2005, 9:18:57 AM10/18/05
to
Hei!

Ser det ikke er allverdens trafikk på denne gruppen, men jeg forsøker meg
likevel:

Jeg har behov for å dele opp strenger i mindre biter til bruk i en
CSV-fil. Eksempel:

<tall>20:15</tall>
<tall>528502.894</tall>
<tall>12.10.2005</tall>

Fra første <tall> trenger jeg følgende output:
20,:,15,,,,,

Fra andre <tall> trenger jeg:
528502,.,894,,,,,

Fra tredje:
12,.,10,.,2005,,,


Jeg vet ikke på forhånd hvilken posisjon skilletegnet kommer til å stå i,
hvor langt hvert nummerfelt er, eller nøyaktig hvor mange felter det er -
men jeg vet at det aldri er mer enn tre skilletegn (og dermed fire
nummerfelter) i en gitt streng. Jeg opererer med en gitt liste mulige
skilletegn (.,-/:). Dersom strengen inneholder andre tegn enn tall og
disse skilletegnene skal den ignoreres. Jeg må selvsagt uansett ende opp
med riktig antall komma, for å ikke rote til CSV-formatet mitt.

Så kommer spørsmålet: Er det i det hele tatt mulig å få til dette med
XSLT/XPath? Og i så tilfelle: er det noen som kunne forbarme seg og gi et
eksempel på hvordan det kan gjøres? Streng-funksjonene virker ganske
primitive, men det finnes kanskje en (forhåpentligvis ikke *altfor*
grisete) vei rundt?


--
Eira Monstad
http://epistel.no/

Erik Andreas Brandstadmoen

unread,
Nov 4, 2005, 3:28:00 PM11/4/05
to
Eira Monstad wrote:
> Hei!
>
> Ser det ikke er allverdens trafikk på denne gruppen, men jeg forsøker
> meg likevel:
>
> Jeg har behov for å dele opp strenger i mindre biter til bruk i en
> CSV-fil. Eksempel:
>
> <tall>20:15</tall>
> <tall>528502.894</tall>
> <tall>12.10.2005</tall>
>
> Fra første <tall> trenger jeg følgende output:
> 20,:,15,,,,,
>
> Fra andre <tall> trenger jeg:
> 528502,.,894,,,,,
>
> Fra tredje:
> 12,.,10,.,2005,,,


Ser jo ut som om du kan bruke denne:

fn:tokenize(string,pattern)

Example: tokenize("XPath is fun", "\s+")
Result: ("XPath", "is", "fun")

evt. i kombinasjon med denne:

fn:string-join((string,string,...),sep)

Returns a string created by concatenating the string arguments and using
the sep argument as the separator

Example: string-join(('We', 'are', 'having', 'fun!'), ' ')
Result: ' We are having fun! '


Noe slik vil vel fungere for første (gitt at du ikke skal ha alle de
kommaene med deg på slutten, da. De får du evt. slenge på selv... ;))

fn:string-join(fn:tokenize(node, ':'), ',')

gitt at node er <tall>-noden din.


mvh. Erik Brandstadmoen


alexander....@gmail.com

unread,
Nov 14, 2005, 9:26:18 PM11/14/05
to
Hei,

> <tall>20:15</tall>

Jeg setter spørsmålstegn ved selve XML skjemaet ditt. Hvorfor et så
til grader supergenerelt element som "tall"? I det minste burde det
være ;

<tall type="tid">20:15</tall>
<tall type="flyttall">528502.894</tall>
<tall type="dato">12.10.2005</tall>

Uansett går det fint an å lage XSLT maler som tar seg av disse
tingene, men er poenget ditt å la XSLT'en gjette seg til hva slags
type elementet er?

Vanligvis lager jeg variabler som autosjekker ;

<xsl:variable name="type-check">
<xsl:choose>
<xsl:when test="substring-after(.':')">time</xsl:when>
<xsl:when
test="substring-after(substring-after(.'.')'.')">date</xsl:when>
<xsl:otherwise>float</xsl:otherwise>
</xsl:choose>

herfra er det enklere å lage variabler som videre prosesserer dataene.
Men jeg skal gi det et annet tips som kanskje løser problemet ditt på
en "enklere" måte ;

<xsl:value-of select="translate(.,'.:',',')" />

Vet ikke hvor nødig du trenger akkurat 7 kommaer i output'en, men hvis
du bruker en kombinasjon av koden fra toppen også, kan du gjøre ;

<xsl:variable name="output">
<xsl:value-of select="translate(.,'.:',',')" />
<xsl:choose>
<!-- etter en translation() på DATO burde det være 3 kommaer igjen
-->
<xsl:when test="$check-type='date'">,,,</xsl:when>
<!-- generelt etter translation() burde det være 5 kommaer igjen
-->
<xsl:otherwise>,,,,,</xsl:otherwise>
</xsl:choose>
</xsl:variable>

<xsl:value-of select="$output" />

Håper dette hjelper.


Alexander

Eira Monstad

unread,
Nov 15, 2005, 6:00:56 AM11/15/05
to
On Tue, 15 Nov 2005 03:26:18 +0100, <alexander....@gmail.com> wrote:

> Hei,
>
>> <tall>20:15</tall>
>
> Jeg setter spørsmålstegn ved selve XML skjemaet ditt. Hvorfor et så
> til grader supergenerelt element som "tall"? I det minste burde det
> være ;
>
> <tall type="tid">20:15</tall>
> <tall type="flyttall">528502.894</tall>
> <tall type="dato">12.10.2005</tall>

Vel, dette var en forenkling for å illustrere. Men det er et poeng at jeg
IKKE vet hva formatet på tallet er i forkant. Du må gjerne si hva du mener
dataene *burde være*, men i praksis kommer det nok helt an på dataenes
opphav og tiltenkte bruk hva de faktisk *er* :)


> Uansett går det fint an å lage XSLT maler som tar seg av disse
> tingene, men er poenget ditt å la XSLT'en gjette seg til hva slags
> type elementet er?

Nei, ikke direkte. Poenget er å splitte på skilletegn, uansett hva
innholdet av strengen er. Strengen kan også inneholde bokstaver, som skal
sorteres bort. Jeg har riktignok noe mer data å gå ut fra enn bare
elementet <tall>, men markupen er på ingen måte en pålitelig indikator på
det nøyaktige formatet til innholdet. Et tidspunkt kan like gjerne være
"20", "20.15" eller for den saks skyld "20.15.45". Det finnes også
forekomster av "kl.20". Jeg vet altså verken hvor mange skilletegn som
finnes i strengen, eller hva slags strengtyper som inneholder hvilke
skilletegn

Jeg kan selvsagt komme unna dette ved å lage generelle variabler som
"topunktum", "trepunktum", "etpunktumogenbindestrek", "trebindestreker"
osv. Det ville løse de fleste problemene mine, men ikke problemet med
andre tegn som skal lukes bort (se lenger ned). Dessuten er det ikke en
videre elegant løsning, og heller ikke særlig enkel å vedlikeholde. Jeg
vil kjapt ende opp med fryktelig mange mulige kombinasjoner.

I og med at jeg trenger output i CSV-format, så er det ganske riktig at
jeg må ha nøyaktig 7 kommaer :) Koden over vil uansett dessverre ikke
fungere, ettersom den ikke tar hensyn til alle problemene i min
opprinnelige post:

"Jeg vet ikke på forhånd hvilken posisjon skilletegnet kommer til å stå i,
hvor langt hvert nummerfelt er, eller nøyaktig hvor mange felter det er -
men jeg vet at det aldri er mer enn tre skilletegn (og dermed fire
nummerfelter) i en gitt streng. Jeg opererer med en gitt liste mulige
skilletegn (.,-/:). Dersom strengen inneholder andre tegn enn tall og
disse skilletegnene skal den ignoreres. Jeg må selvsagt uansett ende opp
med riktig antall komma, for å ikke rote til CSV-formatet mitt."


Jeg ser at jeg var litt uklar. Jeg burde ha skrevet "Dersom strengen
inneholder andre tegn enn tall og disse skilletegnene skal disse tegnene
ignoreres." Jeg burde også presisert at skilletegn som kommer sammen med
andre tegn skal fjernes sammen med disse tegnene. "Kl.20.30" skal altså
bli "20,30,,,,,,".


> Håper dette hjelper.

Ikke veldig, men du skal ha tusen takk for forsøket likevel! Imidlertid
endte jeg opp med å hive XSLT ut vinduet etter noen dager og skrev hele
greia i perl på noen timer i stedet :) Jeg endte med konklusjonen at selv
om det kanskje var teknisk mulig å lage en hack for dette problemet i
XSLT, så egner språket seg rett og slett ikke til strengmanipulering
utover det veldig grunnleggende.

Om noen er interessert, her er perl-koden for å håndtere splitte-problemet
(hele omformingen av XML-filen er selvsagt langt mer omfattende):


# NUMERIC VALUES AND SEPARATORS
my $numberasstring = $sayas->getFirstChild->getNodeValue;
$numberasstring =~ s/^(\D)*//g; # remove everything before the first
number in the string, this solves cases like "kl.19", "E39" etc.
$numberasstring =~ s/(\D)*$//g; # remove everything after the last
number in the string, this solves cases like "1700-talls" etc.
my @numberstring = split(/(\.|\,|=|\-|\/|:)/,$numberasstring);
my $i = 0;
while ($i < 7) {
if ((@numberstring[$i]) && (@numberstring[$i] =~
/^(\d)*$|^(\.|\,|=|\-|\/|:)$/)) { # if the field exists and it contains
only numbers or a separator
my $line = @numberstring[$i];
$line =~ s/,/c/g;
print ("$line,");
}
else {
print ",";
}
$i++;

Eira Monstad

unread,
Nov 15, 2005, 6:03:56 AM11/15/05
to

Hei! Tusen takk for innspill. Jeg har imidlertid gitt opp XSLT for dette
formålet og løst problemet i Perl i stedet :)

Alex

unread,
Nov 15, 2005, 4:15:43 PM11/15/05
to
Hei,

> Koden over vil uansett dessverre ikke
> fungere, ettersom den ikke tar hensyn til alle problemene i min
> opprinnelige post

Heh, koden som den stod fikset problemet du gav, men jeg ser at det var
mer til problemet ditt enn først definert. Det er fullt mulig å
gjøre hele saken i XSLT, men siden du allerede har løst det i et
annet språk får vi la det bli til neste gang.

Når det gjelder XSLT og dens "mangel" på strengmanipulasjon må vi
huske på i hvilken kontekst XSLT ble laget; for å håndtere XML, ikke
strenger. Man la også sterk vekt på at standarden skulle raskt bygges
ut etter versjon 1 for å plukke opp brukersaker som manglet i første
treff. Desverre ble versjon 1.1 kansellert (halveis politisk, halveis
teknologisk basert), og 2.0 er fortsatt på trappene.

Når det er sagt er versjon 2.0 akkurat det du trenger i den forstand
at den har regulære uttrykk og bedre strengehåndteringsfunksjoner.

Og avslutningsvis vil jeg også påpeke at det er *ingenting* som ikke
kan gjøres i XSLT, selv om det kanskje ikke er best egnet til det. :)


Alex

0 new messages