Autor Zpráva
Dox
Profil *
Ahoj,

mám následující strukturu:
{tab=Text1}
<p> nejaky</p><p>dlouhy text 1</p> ...

{tab=Text2}
<p>Jiny text....</p>

{tab=Text2}
....
{/tab}
Potřeboval bych uložit do pole jednotlivý text nacházející se v tab.
Například tedy $result['Text1'] by měl obsahovat "<p> nejaky</p><p>dlouhy text 1</p> ..."
Značky jako Text1, Text2, ... můžu znát a mám je v proměnné.
Mohl by mě prosím někdo popstrčit jak to vyřešit?

Předem moc děkuji.
mimochodec
Profil
{tab=Text2} Tam je opravdu dvakrát? {/tab} tam je opravdu jednou?
Karel N.
Profil *
Tady je řešení s použití regulárního výrazu, jednodušší řešení je si projít řádek po řádku, matchovat je a podle toho zařazovat do stacku, to si ale každý umí napsat určitě sám.

Regálární výraz /(?:{tab=(?P<tab>[^}]+)})\n(?P<content>(?:(?!\n{\/?tab[^}]*}).)*)/s zachytí do proměnné tab jednotlivé názvy tabů a do proměnné content jejich hodnoty, pole je možné spojit přes jejich indexy, viz kód níže. Regulární výraz potřebuje jediný přepínač a to s, který matchne celý text jako jednu řádku (v případě použití v jiném jazyku/prostředí je nutné přidat přepínač g, který v php je implikovaný funkcí preg_match_all).

V případě zájmu mohu regulární výraz rozložit a okomentovat, kdybyste někdo chtěli znát tu magii.

<?php

$text = "{tab=Text1}
<p> nejaky</p><p>dlouhy text 1</p> ...
 
{tab=Text2}
<p>Jiny text...{test}.</p>
 
{tab=Text3}
....
{/tab}
{tab=Text4}
<p> nejaky</p><p>dlouhy text 1</p> ...
 
";

$pattern="/(?:{tab=(?P<tab>[^}]+)})\n(?P<content>(?:(?!\n{\/?tab[^}]*}).)*)/s";
preg_match_all($pattern, $text, $matches);

$result = [];

if($matches) foreach($matches["tab"] as $i => $name) {
    if(isset($result[$name])) throw new Exception("Duplicate tab name '{$name}'");
    $result[$name] = $matches["content"][$i];
}

var_dump($result);

a výsledek vypadá takhle:
array(4) {
  ["Text1"]=>
  string(40) "<p> nejaky</p><p>dlouhy text 1</p> ...
 "
  ["Text2"]=>
  string(28) "<p>Jiny text...{test}.</p>
 "
  ["Text3"]=>
  string(4) "...."
  ["Text4"]=>
  string(41) "<p> nejaky</p><p>dlouhy text 1</p> ...
 
"
}

[#2] mimochodec ano, je to nevhodně zapsané. Text2 jsem opravil a přidal do ukázkového kódu kontrolu, ukončovací {/tab} je nepovinný. V textu se může vyskytovat cokoliv kromě otevíracího a ukončovacího tagu.
Dox
Profil *
Karel N.:
Moc děkuji za odpověď. Zadaný příklad mi bohužel nefunguje. Vrací mi prázdné pole. Bylo by prosím možné ho upravit? A trochu mi vysvětlit daný regulární výraz?

Předem moc děkuji...
Karel N.
Profil *
kód, který jsem napsal řádně funguje od php 5.4.0 po 5.6.3+ vč. hhvm 3+. Pokud ti nejde, dáváš tam jiný vstup (text) než jsi nám sem napsal. Můžeš sem hodit data, které se s ním pokoušíš přečíst?
Dox
Profil *
Když zkopíruju celý kód, který tady je, tak mi to vrátí prázdné pole..bez chyby - verze PHP je 5.5. Zkoušel jsem to i na serveru a výsledek stejný. Chybové hlášky mám zapnuty a vstup dávám ten Tvůj.
Karel N.
Profil *
To jsi mě dostal, v tom bude nějaká moucha s kopírováním. Pokud udělám shodný postup, jde mi to. Kód jsem nakopíroval do 3v4l a také bez problémů vč. správného fungování v očekávanách verzích php. Viz http://3v4l.org/A6f5I. V čem to kopíruješ? Pspad? Neměl by být ani problém s kódováním, zkus to prosím ještě zkopírovat znovu. Prázdné pole znamená, že nedošlo k matchnutí reguláru.
Dox
Profil *
Vážně zajímavý...Kopíruji to do PSPadu s kódováním UTF-8 .. bez jakékoliv HTML hlavičky (s ní to také nefunguje). Na serveru (Wedos) je verze PHP 5.4.32 a na localhostu mám 5.5.15. Nerozumím tomu...Bylo by možné mi poslat celý soubor?


Když si dám var_dump($matches). Výsledek je:
array(5) {
  [0]=>
  array(0) {
  }
  ["tab"]=>
  array(0) {
  }
  [1]=>
  array(0) {
  }
  ["content"]=>
  array(0) {
  }
  [2]=>
  array(0) {
  }
}
array(0) {
}



Ještě přidávám odkaz na php sandbox, kde mi to taky vrací prázdné pole.
http://sandbox.onlinephpfunctions.com/code/04b42b6f0be42f059e6859370b17b1601e3e62dc
Karel N.
Profil *
Soubor jsem hodil sem http://95.85.37.63/parse.php. Funkčnost jsem ověřil i na wedosu, ale tady bude dělat nějaké komplikace utf8 a pspad, neumím si představit co tam dělá ještě za operace, pokud soubor uložím v utf-8, utf-16, ANSI, ISO-8859-1 vše samozřejmě jede a hlavně na běh php 5.4 to nemá vliv, stejně jak na parsování regulárů.
Dox
Profil *
Moc děkuji. Tvůj kód šlape jak má.. Když ho ale přeuložim u sebe, tak uz nefunguje. Opravdu to tedy dělá PSPad. Jaký jiný volný editor bych mohl použít? Případně nejde ten reg. výraz přepsat nějak jinak?
Karel N.
Profil *
Aj, problém dělá znak nového řádku, v php se nerozlišuje a přepínač /R bere příliš moc možností. Zkus vyměnit tuhle řádku a mělo by ti to jít i kopírovat do pspadu:
$pattern="/(?:{tab=(?P<tab>[^}]+)})(?:\r|\n|\r\n)(?P<content>(?:(?!(?:\r|\n|\r\n){\/?tab[^}]*}).)*)/s";

Místo pspadu bych doporučil používat NetBeans nebo Pphstorm, daleko více ti asistují s psaním kódu.

Je více možností jak provést tohle parsování, regulární výrazy mám rád, jsou deklarativní, velice rychlé, ale jsou složitější na pochopení.
Dox
Profil *
Super, tohle již funguje, jenomže když to použiji na reálných datech, tak to zkolabuje a prohlížeč mi vrátí: ERR_CONNECTION_RESET - žádná odezva z php strany.. To je zkrátka prokletý. Navíc značky {tab=xx} jsou ještě uzavřeny v <p> tagech - <p>{tab=xx}</p> a uvnitř {tab=xx}a{tab=yy} může být cokoliv, tabulky, seznamy, apod a nemusi to byt v <p>.. Akorát vůbec nerozumím tomu výrazu, tak nevím jak to upravit..Bylo by prosím možné ho ještě pozměnit?
Karel N.
Profil *
hoď někam celý ten soubor, který zkoušíš načíst. S parsováním nějakou textového souboru jsou vždy nějaké problémy a musíš dobře znát jeho obsah a gramatiku. Nedá se napsat něco univerzálního co snese všechny blbosti.

Pokusil jsem se ti popsat regulární výraz
$pattern="/
    (?:                        #celý obsah závorek bere jako skupinu (jeden celek) a díky '?:' ho nebude vracet v matches
        {tab=(?P<tab>[^}]+)}   #předpis pro zápis tabu. Název tabu uloží na výstupu do pole 'tab',
                               #v názvu může být cokoliv kromě ukončovacího }
    )                          #uzavření skupiny
    (?:\r|\n|\r\n)             #znak nového řádku. Obsah tabu začíná až na dalším řádku. Musíme počítač s windows, mac a unix
    (?P<content>               #za novým řádkem následuje obsah, ten si opět uložíme na výstupu do pole 'content'
        (?:                    #vytváříme novou skupinu, která nám ohraničuje obsah tabu. Obsah již máme uložený v 'content',
                               #proto používáme '?:', aby skupina se znovu neuložila na výstup
            (?!                #řiká nám, že má vzít vše (a předat do nadřazeného 'content') dokud nenarazí na nový tag
                               #nebo na uzavření toho současného
                (?:\r|\n|\r\n) #nový tag nebo uzavírací musí začínat na novém řádku
                {\/?tab[^}]*}  #definice pro odchycení nového nebo uzavíracího tagu
            ).
        )*                     #tag nemusí mít žádný obsah
    )                          #uzavření definice obsahu
/xs";
Dox
Profil *
Na malých datech to funguje správně, ale když se tím snažím prohnat delší text, spadne prohlížeč :-D .. což nechápu, jak je to možný .. V Chromu se vypíše: "Tato webová stránka není dostupná." přitom jsem smazal var_dump, aby se nic nevypisovalo..a stejně to spadne...
Existuje prosím nějaký jiný způsob? I třeba bez reg. výrazů.
Karel N.
Profil
a jaký máš memory limit v php? Mrkni do logu, tam budeš mít důvod proč to spadlo, nejedná se o pád prohlížeče, ale php. O kolik dat se jedná? Jsou to desítky MB? Samozřejmě výpočet nějak čas/paměť zabere, tenhle regulár je docela žrout, ale rozhodně je méně náročnější než ruční parsování. Paměťová náročnost je přibližně 3x větší než je zdrojový soubor dat.

Samozřejmě, že existuje několik způsobů, ale tohle je pro mě nejsnáze zapsatelný. Parsovat se to dá i streamovaně, řádek po řádku, ale teď nemám čas ti sem kód k tomu psát. K parsování se dá použít peg, ale na ovládání je složitější než reguláry. Nebo můžeš jít podobným způsobem jako je tady http://stackoverflow.com/a/5299502. V php je k podobným účelům interní funkce strtok. Možností je nespočet.

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: