Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

> PCRE Jak wydobyć tagi nie mające domknięcia, preg_match
starach
post 23.10.2014, 16:20:30
Post #1





Grupa: Zarejestrowani
Postów: 999
Pomógł: 30
Dołączył: 14.01.2007
Skąd: wiesz ?

Ostrzeżenie: (0%)
-----


Cześć,

Mam kod HTML w którym muszę wykryć tag nie mający zamknięcia na jednej linii domknąć go i skopiować jego część otwierającą na następną linie.
np.
Cytat
mój <span class="pink">różowy pies
ma<span>
5 <span class="blue">niebieskich</span> łat
<span class="grey">szary</span> ogon, a jego <span class="brown>brązowe oczy,
również są</span>
<span class="green">zielone</span>
Do postaci:
mój <span class="pink">różowy pies</span>
<span class="pink">ma<span>
5 <span class="blue">niebieskich</span> łat
<span class="grey">szary</span> ogon, a jego <span class="brown>brązowe oczy,</span>
<span class="brown>również są</span>
<span class="green">zielone</span>


Muszę w tym celu wykryć dodatkowe tagi otwierające, które nie mają na danej linii pary. Idzie mi jak po grudzie. Kod poniżej to udowadnia. Jak wyłuskać te niedomknięte tagi na danej linii przy użyciu wyrażeń regularnych?

  1. $str = '<span class="blabla">blabla <span class="two">2.two</span></span><span class="three">3.three</span><span class="four">4.four';
  2. $patt = '#(?P<tag_o><span.*?>)(?P<cnt>.*?(?R)?)(?P<tag_c></span>)#i';
  3.  
  4. $a = preg_match_all($patt, $str, $aMatch);
  5.  
  6. foreach($aMatch as $key => $val) {
  7. if(is_numeric($key)) {
  8. unset($aMatch[$key]);
  9. }
  10. }
  11. var_dump($a, $aMatch);
  12. echo '-------------------------------<br/><br/>';
  13. $patt = '#(?P<tag_o><span.*?>)(?P<cnt>.*?(?R)?)(?P<tag_c></span>)(?P<tag_o2><span.*?>)(?R)?(?P<cnt2>.*?(?R)?)#i';
  14.  
  15. $a = preg_match_all($patt, $str, $aMatch);
  16.  
  17. foreach($aMatch as $key => $val) {
  18. if(is_numeric($key)) {
  19. unset($aMatch[$key]);
  20. }
  21. }
  22. var_dump($a, $aMatch);
Go to the top of the page
+Quote Post
 
Start new topic
Odpowiedzi (1 - 7)
viking
post 23.10.2014, 17:33:06
Post #2





Grupa: Zarejestrowani
Postów: 6 380
Pomógł: 1116
Dołączył: 30.08.2006

Ostrzeżenie: (0%)
-----


A co chcesz uzyskać? Ja bym to przepuścił przez http://php.net/manual/en/book.tidy.php


--------------------
Go to the top of the page
+Quote Post
kreatiff
post 23.10.2014, 18:39:23
Post #3





Grupa: Zarejestrowani
Postów: 324
Pomógł: 105
Dołączył: 7.08.2012

Ostrzeżenie: (0%)
-----


Może HTML Purifier?
Ogólnie regex i html to stąpanie po cieńkim lodzie sad.gif

Ten post edytował kreatiff 23.10.2014, 18:43:06
Go to the top of the page
+Quote Post
starach
post 24.10.2014, 06:11:50
Post #4





Grupa: Zarejestrowani
Postów: 999
Pomógł: 30
Dołączył: 14.01.2007
Skąd: wiesz ?

Ostrzeżenie: (0%)
-----


Niestety ani purifier ani tidy nie robią tego na czym mi zależy.

Przykłady:
Działa:
  1. $str = '<span>A</span> <span>B</span> <span>C';
  2. $patt = '#<span>.(?!</span>)#';
  3.  
  4. preg_match_all($patt, $str, $aMatch);
  5. var_dump($str, $patt, $aMatch);
  6. // Wynik: <span>C

Nie działa:
  1. $str = '<span>Aa</span> <span>Bb</span> <span>Cc';
  2. $patt = '#<span>.*(?!</span>)#';
  3.  
  4. preg_match_all($patt, $str, $aMatch);
  5. var_dump($str, $patt, $aMatch);
  6. // Wynik: <span>Aa</span> <span>Bb</span> <span>Cc


Zmniejszenie zachłanności przez dodanie ? po * też nie przynosi oczekiwanych rezultatów.

Potrzebny mi ostatni tag <span> który nie ma zamknięcia.

Ten post edytował starach 24.10.2014, 06:15:13
Go to the top of the page
+Quote Post
kreatiff
post 24.10.2014, 11:20:04
Post #5





Grupa: Zarejestrowani
Postów: 324
Pomógł: 105
Dołączył: 7.08.2012

Ostrzeżenie: (0%)
-----


A takie coś?
  1. $str = '<span>Aa</span> <span>Bb</span> <span>Cc';
  2. $patt = '#</span>((?:(?!</span>).)*)$#';
  3.  
  4. preg_match_all($patt, $str, $aMatch);
  5. echo'<pre>',var_dump($str, $patt, $aMatch),'</pre>';
Go to the top of the page
+Quote Post
starach
post 24.10.2014, 12:30:04
Post #6





Grupa: Zarejestrowani
Postów: 999
Pomógł: 30
Dołączył: 14.01.2007
Skąd: wiesz ?

Ostrzeżenie: (0%)
-----


kurcze dzięki, działa... tylko dlaczego to działa? :|
Go to the top of the page
+Quote Post
kreatiff
post 24.10.2014, 13:14:51
Post #7





Grupa: Zarejestrowani
Postów: 324
Pomógł: 105
Dołączył: 7.08.2012

Ostrzeżenie: (0%)
-----


Postaram się tak na chłopski rozum opisać, ale nie obiecuję, że się rozjaśni wink.gif

Kluczowe są tutaj:
$ - koniec linii/stringa
(?!</span>). - dowolny znak (kropka), który nie jest poprzedzony </span>, dalsza * oznacza 0 lub więcej takich znaków
?: to po prostu pominięcie łapania danej grupy (wynik łapania z tego nawiasu nie trafia do $aMatch)

Nasze wyrażenie szuka w ciągu takiego </span>, po którym już to </span> więcej nie wystąpi. I wówczas wszystko po tym zamykającym tagu jest łapane. Jeśli po pierwszym </span> silnik regex znajdzie kolejne </span>, to omija to co napotkał do tej pory i zaczyna dopasowywanie od tego kolejnego domknięcia tagu span. Itd. aż nie trafi na domknięcie do samego końca ciągu, co w konsekwencji oznacza, że wszystko co złapał jest tym co nas interesuje.

Ten post edytował kreatiff 24.10.2014, 13:18:34
Go to the top of the page
+Quote Post
starach
post 24.10.2014, 13:35:11
Post #8





Grupa: Zarejestrowani
Postów: 999
Pomógł: 30
Dołączył: 14.01.2007
Skąd: wiesz ?

Ostrzeżenie: (0%)
-----


  1. $patt = '(?:(?!</span>).)*';

No tak kropka pobiera dowolny znak natomiast negative lookahead wymusza żeby znaleziony znak nie był częścią taga zamykającego. Natomiast * oczywiście powtarza znaleziony i przefiltrowany już znak. Tak proste że aż mi głupio że na to nie wpadłem. Dzięki wielkie. smile.gif

Ten post edytował starach 24.10.2014, 13:35:41
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 14.08.2025 - 06:25