Hei!
Minulla on ongelma säännöllisten lausekkeiden kanssa pienimuotoinen ongelma. Minulla on dataa tässä muodossa:
[...][...=...][...=...][/...] [Kaupungintalo][taso=4][valmistuu=1272728202][/Kaupungintalo][Kauppa][taso=14][valmistuu=1272728252][/Kauppa]
Minun pitäisi saada tehtyä tälläinen taulukko tuosta datasta:
array( 0 => array( nimi => Kaupungintalo, taso => 4, valmistuu => 1272728202 ), 1 => array( nimi => Kauppa, taso => 14, valmistuu => 1272728252 ) );
Miten saisin tehtyä tälläisen?
Olen koittanut näin:
/\[[a-zA-Z]\]\[taso=[0-9]{1,2}\] \[valmistuu=[0-9]\]\[\/[a-zA-Z\]/
Se ei vaan oikein toimi. En tiedä miten pitäisi menetellä, kun samassa merkkijonossa on paljon samanlaisia asioita.
Macro kirjoitti:
Olen koittanut näin:
/\[[a-zA-Z]\]\[taso=[0-9]{1,2}\] \[valmistuu=[0-9]\]\[\/[a-zA-Z\]/
Tuossa määritellään, että ensimmäisten hakasulkujen sisällä pitää olla tasan yks merkki a-z tai A-Z. Eli
[m] kelpaisi [kauppa] ei kelpaa.
Jos haluat etteä määrittämääsi merkkiä on yksi tai useampi niin +, jos taas merkkejä ei ole pakko olla yhtään (eli 0 tai useampi) niin *
Esim.
\[[a-zA-Z]+\]
Lisäksi lausekkeessasi on ylimääräinen välilyönti ja lopusta puuttuu sulkeva hakasulku. Grezin mainitsema virhe toistuu myös kohdassa "valmistuu".
Hassua, että aina pitää kehitellä omia formaatteja, vaikka valmiitakin on jo vaikka millä mitalla. Esimerkiksi JSON sopisi tuohon tilanteeseen nähdäkseni oikein hyvin, ja samalla säästyttäisiin ylimääräisiltä rajoitteilta datan sisällössä (esim. nythän ]-merkki olisi melkoinen ongelma). PHP:ssä on valmiiksi funktiot json_encode ja json_decode. Jos datan on tarkoitus olla käsin muokattavissa, suosittelen myös päiväysten tallentamista MySQL:stä tutussa formaatissa; PHP:llä aikaleiman voi silloin lukea funktiolla strtotime ja luoda funktiolla date formaatilla "Y-m-d H:i:s"
.
[ { "nimi": "Pyramidi", "taso": 7, "valmistuu": "2010-04-29 15:46:37" }, { "nimi": "Savupirtti", "taso": 1, "valmistuu": "1600-06-21 19:42:31" } ]
Ei ole tarkoitus tehdä tollaista. Ei sotketa JavaScriptiä mukaan.
Miten niin käyttää omia aikaleimojaan? Tuo on ihan normaali unix timestamppi.
JSON ei ole mitenkään JavaScriptiin sidoksissa, vaan sitä voi muutenkin käyttää yksinkertaisena ja selkeänä tallennusmuotona datalle, joka sisältää lukuja, tekstiä, taulukoita ja assosiatiivista dataa. Esimerkiksi tuon sinun datamuotosi se korvaisi hyvin, eikä tarvitsisi itse kirjoitella parseria, generaattoria ja suurta määrää tarkistuksia datan muodosta.
En ole puhunut omista aikaleimoista vaan omista formaateista. Aikaleiman mainitsin vain käsin muokkaamisen kannalta: jos tiedostoa muokkaa tekstieditorilla, UNIX-aikaleima ei ole kovin käytännöllinen, koska harva pystyy niitä päässään laskemaan.
Ei ole tarkoitus käsin muokata, käyttäjälle ei edes saa näyttää kyseistä taulukkoa. En ymmärrä miksi se pitäisi kasata noin, yhtä hyvin voisin kirjoittaa sen taulukkona suoraan tietokantaan enkä tuolla JSON-formaatilla. Ihan vaan periaattenkin vuoksi haluan tietää, miten se käsitellään tuossa.
Miten pystyn jakamaan tiedoston paloihin siten, että se katkeaa aina [/...] kohdalla? Sisällöstä riippumatta kumminkin.
<?php $data = preg_split('#\\[/.*?\\]#', $data);
Kiitos! En tiennytkään tuollaista funktiota. :)
<?php $example = "[Kaupungintalo][taso=4][valmistuu=1272728202][/Kaupungintalo]". "[Kauppa][taso=12][valmistuu=1272828262][/Kauppa]"; $explode = preg_split('#\\[/.*?\\]#', $example); echo $example . "<br><br>"; for($i = 0; $i < sizeof($explode) - 1; $i++) { $pieces = explode(" ", preg_replace("/\[(.*?)\]\[taso=(.*?)\]\[valmistuu=(.*?)\]/", "$1 $2 $3", $explode[$i])); echo "{$pieces[0]} {$pieces[1]} {$pieces[2]}<br>"; } ?>
Koodissasi on kaksi ongelmaa:
1. Välilyönti hakasulkujen sisällössä toimii exploden takia väärin.
2. Virheellinen data aiheuttaa ongelmia, koska et tarkista sitä.
Molemmat ongelmat häviävät, kun vaihdat preg_replace-funktion tilalle preg_match-funktion, jonka paluuarvosta selviää, oliko teksti lausekkeen mukainen, ja joka tallentaa kolmantena parametrina annettuun muuttujaan nuo suluissa olevat osat.
Suosittelen kaksinkertaisen kenoviivan käyttöä lausekkeissa, kuten itse tein. Tälle on yksinkertainen syy: PHP-tulkki muuttaa \\:n yhdeksi \:ksi, joka siis kuuluu lausekkeen osaksi. Nuo muut merkinnät toimivat vain, koska PHP ei tunnista esimerkiksi \]:n tarkoittavan mitään erityistä ja jättää sen paikalleen. Entä jos haluaisitkin lausekkeeseen merkit \0? Tällöin joutuisit kuitenkin kirjoittamaan \\0, jolloin lausekkeesta tulisi epäjohdonmukainen, kun osa kohdista olisi merkitty eri tavalla. Lisäksi jos PHP:n kehittäjät joskus keksivät lisätä uusia escape-merkintöjä (kuten nyt \]), nykyinen lausekkeesi voikin yllättäen lakata toimimasta.
<?php $example = "[Kaupungintalo][taso=4][valmistuu=1272728202][/Kaupungintalo][Kauppa][taso=12][valmistuu=1272828262][/Kauppa]"; $explode = preg_split('#\\[/.*?\\]#', $example); echo $example . "<br><br>"; for($i = 0; $i < sizeof($explode) - 1; $i++) { preg_match("/\\[(.*?)\\]\\[taso=(.*?)\\]\\[valmistuu=(.*?)\\]/", $explode[$i], $pieces); echo "{$pieces[1]} {$pieces[2]} {$pieces[3]}<br>"; } ?>
Näin siis?
Joo. Vielä voisit tarkistaa, että preg_match palauttaa arvon 1 merkiksi siitä, että teksti oli lausekkeen mukainen ja osat saatiin talteen. (Jos data tulee aina omista lähteistä, tarkistus ei ole välttämätön mutta silti hyvä bugien varalta.)
Toisaalta voisit myös muuttaa koko systeemin käyttämään funktiota preg_match_all, jolloin et tarvitsisi ollenkaan preg_splittiä etkä ehdottamaani tarkistusta vaan saisit kaikki kelvolliset tulokset kerralla taulukkoon. Lausekkeen loppuun voisi tällöin varmuuden vuoksi lisätä tuon preg_splitissä olevan osuuden.
Kuten huomaat, toteutustapoja on taas vaikka millä mitalla. :)
Juu, kiitos avusta :)
Aihe on jo aika vanha, joten et voi enää vastata siihen.