webForumDet fria alternativet

Formatera radbrytningar från databas + utf8-fråga

PHP

7 svar · 682 visningar · startad av bassebhu

Medlem sedan nov. 20016 480 inlägg
Frågan#1

Hej alla glada!

När jag hämtar ut text från en databas, finns det då något smart sätt att ersätta radbrytningar så att det formateras med <p> och <br />?

Jag använder nu följande:

function contentreplace($content) {

	$content = str_replace("\r\n\r\n", "</p>\n<p>", $content);
	$content = str_replace("\r\n", "<br />", $content);
	$content = '<p>' . $content . '</p>';
	return $content;

}

echo contentreplace($row['content']);

Men det måste ju finnas smartare sätt? Typ en regexp-snutt. Nackdelen med min är nämligen att om jag blandar in <dl> eller <ul> m.m. i databasen så kommer det att generera <p><dl></dl></p>, ja ni förstår. Jag vill alltså att replacen bara ska gälla för saker som inte ligger inom <dl> och <ul>. En radbrytning = <br />, två = <p>text</p>, men då som sagt inte om det är inom ovanstående element.

Om sidans doctype är utf-8, om databasen är utf-8, om tabellen är utf-8 och om fältet är utf-8, ska jag då behöva använda utf8_decode() varje gång jag lägger in nåt i databasen samt utf8_encode() när jag vill skriva ut det? Jag vet inte vad dessa gör egentligen, men jag vet att det funkar när jag kör det, men har fått strul när jag inte har haft dem med. Vad kan ha gått galet?

Tackar för svar!

Medlem sedan juni 20008 205 inlägg
#2
  1. Testa med preg_replace("/(.+)/", "<p>$1</p>", $strängen). Punkten ska normalt matcha alla tecken som inte är radbrytningar, så det där innebär att du tar alla sekvenser som inte är radbrytningar och stoppar dem inom p-taggar.
Medlem sedan nov. 20016 480 inlägg
#3

Tack för svaret... men om jag i databasen har t.ex.

Min lista:

<ul>
<li>Listval</li>
</ul>

så returnerar det med den lösningen typ:

<p>Min lista:</p>
<p></p>
<p><ul></p>
<p><li>Listval</li></p>
<p></p>
<p></ul></p>

i stället för som jag vill:

<p>Min lista:</p>
<ul>
<li>Listval</li>
</ul>

Jag vill att en radbrytning i databasen = <br /> och två = <p>text</p>. Sen om det förekommer något inom taggar såsom t.ex. en lista så ska det inte läggas inom <p>. Det borde ju vara möjligt?

Medlem sedan juni 20008 205 inlägg
#4

bassebhu skrev:

Jag vill att en radbrytning i databasen = <br /> och två = <p>text</p>. Sen om det förekommer något inom taggar såsom t.ex. en lista så ska det inte läggas inom <p>. Det borde ju vara möjligt?

Hm, det är det där med "om det förekommer något inom taggar" som komplicerar det hela en aning, eftersom regexpar i det generella fallet inte kan hålla reda på om något ligger inom taggar eller inte. Det du får göra är att antingen göra en ful-workaround som funkar för de värden som du vet att din indata kommer anta, eller så får du göra en simpel markupparser som kan avgöra om text ligger inom taggar eller inte.

Medlem sedan nov. 20016 480 inlägg
#5

spango skrev:

Hm, det är det där med "om det förekommer något inom taggar" som komplicerar det hela en aning, eftersom regexpar i det generella fallet inte kan hålla reda på om något ligger inom taggar eller inte. Det du får göra är att antingen göra en ful-workaround som funkar för de värden som du vet att din indata kommer anta, eller så får du göra en simpel markupparser som kan avgöra om text ligger inom taggar eller inte.

Jag kan mycket väl tänka mig en ful-lösning :)

Några tips på vägen hur jag gör antingen en workaround för <ul> och <dl> eller en markupparser? :)

Tack!

Medlem sedan juni 20008 205 inlägg
#6

Det går ju att göra regexpen så att den inte tar med rader som innehåller < eller >, typ

preg_replace("([^<>\r\n]+)(\r|\n|\r\n)", "$1<br />", 
             preg_replace("([^<>\r\n]+)(\r\r|\n\n|\r\n\r\n)", "<p>$1</p>", $strängen));

Att göra en markupparser är mer jobb, inte helt otippat :) Pseudokod är ungefär:

// texten att parsa
$text = "text <tagg> mer text <undertagg> sen ett taggelement igen <tagg3> nu är vi inom tre taggar </tagg3> </undertagg> text igen </tagg> text utanför tagg <tagg>och innanför tagg</tagg>";
// "tokenize" till en array så att varje tagg är ett enskilt element i arrayen
$tokens = preg_split("/(<[^>]+>)/", $text, PREG_SPLIT_DELIM_CAPTURE);
// stack med de taggar vi är i
$tagStack = array();
foreach ($token in $tokens){
    if (is_start_tag($token)) {
        array_push($tagStack, tag_name($token)); // starttagg, allt som kommer efter ligger inom denna tills vi träffar på sluttaggen
    } else if (is_end_tag($token)) {
        // kolla att taggen vi försöker stänga matchar den senaste icke-stängda taggen
        $stackTop = array_pop($tagStack);
        $closed = tag_name($token);
        if ($stackTop != $closed)
            parse_error("Unmatched start/end tags: Current tag is $stackTop, tried to close with $closed");
    } else if (is_empty_tag($token)) {
        // $token är en tom tagg, avslutas direkt så vi behöver inte uppdatera stacken
    } else {
        // $token är inte en tagg
        if (count($tagStack) == 0) {
            // $token är en friliggande sträng, inte under en tagg
        } else {
            // $token ligger under en tagg
        }
    }
}

tag_name, is_start_tag och sånt är förstås inte funktioner som finns. Men tanken är att man först delar upp texten i element, så att texten i exemplet blir typ:

array(
    "text ", 
    "<tagg>",
    " mer text ",
    "<undertagg>",
    " sen ett taggelement igen ",
    "<tagg3>",
    " nu är vi inom tre taggar ",
    "</tagg3>",
    " ",
    "</undertagg>",
    " text igen ",
    "</tagg>",
    " text utanför tagg ",
    "<tagg>",
    "och innanför tagg",
    "</tagg>"
);

Sen går man igenom element för element, och har en stack som motsvarar vilka taggar man är i. På toppen av stacken ligger alltid den tagg man är i, som botten är den "översta" taggen (rotelementet). När man i exemplet ovan kommer till texten " nu är vi inom tre taggar " är stacken array("tagg", "undertagg", "tagg3"), och när man sen träffar på </tagg3>, så tar man bort sista elementet i stacken (eftersom taggen stängs), så den blir array("tagg", "undertagg")

Är stacken tom (count($tagStack) == 0) är man inte inuti någon tagg alls.

Hänger du med? ;)

Medlem sedan nov. 20016 480 inlägg
#7

hehe, tack för hjälpen, men jag kan inte påstå att jag riktigt hänger med.

Jag testade bara att kopiera in exemplen och fick inget av dem att fungera så jag måste först fråga om det inte fungerar med min php-version: 5.2.4?

När jag sätter $strängen = 'test'; så returneras ingenting alls i första exemplet.
Andra exemplet gör bara att sidan inte laddas. Jag försökte lägga dit error_reporting(E_ALL); först i dokumentet men fick ändå inte fram något felmeddelande.

Fungerar båda exemplen för dig eller var det inte ens meningen att de skulle kunna fungera utan att ändra nåt?

Återigen tack för hjälpen.

Medlem sedan juni 20008 205 inlägg
#8

bassebhu skrev:

När jag sätter $strängen = 'test'; så returneras ingenting alls i första exemplet.

Nu när jag tänker efter måste regexparna ligga inom slashar:

preg_replace("/([^<>\r\n]+)(\r|\n|\r\n)/", "$1<br />", 
             preg_replace("/([^<>\r\n]+)(\r\r|\n\n|\r\n\r\n)/", "<p>$1</p>", $strängen));

bassebhu skrev:

Fungerar båda exemplen för dig eller var det inte ens meningen att de skulle kunna fungera utan att ändra nåt?

Andra exemplet var pseudokod, så nej, tanken var inte att det skulle funka ;)

Regexparna däremot borde funka (om du ändrar felet jag beskrev ovan), har dock inte tillgång till PHP så jag har inte den blekaste aning om ifall just mina funktionsanrop är precis som de ska vara, men jag har testat själva regexparna i annan miljö och de tycks funka. (Åtminstone för ditt exempel...)

268 ms totalt · 4 externa anrop · v20260731065814-full.86ec41c2
125 ms — deklarationer (db)
0 ms — hämta statistik (cache)
141 ms — hämta tråd, inlägg och bilagor (db)
120 ms — ändringar (db)