Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP][Regexp] Zostaw tylko (X) linków w tekscie
Forum PHP.pl > Forum > PHP
barat
Witam. Mam pewną zagwostkę. Powiedzmy, że mam taki tekst:

  1. Lorem ipsum dolor sit <a href="#">amet</a>, consectetur adipiscing elit. Proin <a href="#">amet</a> elementum odio eget mauris ultricies vulputate. Suspendisse scelerisque vulputate risus ac lobortis. <a href="#">Aenean</a> euismod urna at libero vehicula non sollicitudin elit luctus. Aliquam ultricies nisi ac sapien tempus imperdiet sit amet ut nulla. Proin aliquam blandit libero eu ornare. Suspendisse a erat ligula. Phasellus ultricies odio nec metus dictum eget luctus augue interdum. Morbi at turpis libero, imperdiet iaculis sapien. In sed sapien eget turpis semper imperdiet. <a href="#">Nulla</a> iaculis blandit lorem, eget laoreet mauris euismod sed. Vivamus hendrerit euismod tellus, in adipiscing lectus eleifend in. <a href="#">Aliquam</a> imperdiet placerat orci ac ultrices. Curabitur eu sem tortor, at dapibus dolor.


Jakie wyrażenie zastosować, by zostawić tylko 1/2/3/4/X linki w tym tekście, a pozostałe zlikwidować i zostawić tylko tekst pomiędzy <a></a> ?
Oczywiście przyjmując, że:
  • Zostawiamy linki "od lewej" czyli 1/2/3/4/X pierwszych
  • Nigdy nie wiem ile linków zostało wpisanych
  • Linki nie koniecznie muszą być różne (np. 2 wystąpienia <a href="#">amet</a> + kilka innych linków. Mogą być także obok siebie)
  • Linki mogą mieć dodatkowe atrybuty np: <a href="#" target="_blank" title="title" onclick="" class="" id="">aaa</a>


Czyli np dla "pseudofunkcji" zostaw_linki(2) efekt (dla tekstu powyżej) byłby:

  1. Lorem ipsum dolor sit <a href="#">amet</a>, consectetur adipiscing elit. Proin <a href="#">amet</a> elementum odio eget mauris ultricies vulputate. Suspendisse scelerisque vulputate risus ac lobortis. Aenean euismod urna at libero vehicula non sollicitudin elit luctus. Aliquam ultricies nisi ac sapien tempus imperdiet sit amet ut nulla. Proin aliquam blandit libero eu ornare. Suspendisse a erat ligula. Phasellus ultricies odio nec metus dictum eget luctus augue interdum. Morbi at turpis libero, imperdiet iaculis sapien. In sed sapien eget turpis semper imperdiet. Nulla iaculis blandit lorem, eget laoreet mauris euismod sed. Vivamus hendrerit euismod tellus, in adipiscing lectus eleifend in. Aliquam imperdiet placerat orci ac ultrices. Curabitur eu sem tortor, at dapibus dolor.
thek
1. Najpierw policz ile tych linków jest (choćby preg_match_all)
2. Jeśli mieści się w limicie - zostaw tekst
3. Jeśli nie, musisz znaleźć zamknięcie ostatniego dozwolonego i cały tekst za nim pozbawić tagów A.

Wydaje się proste? Nie do końca tak jest. Wszystko dlatego, że tylko str_replace posiada parametr OFFSET. Najwygodniejszy zaś jest preg_replace, który go nie posiada.. Na dodatek musisz przechwycić pozycję zamknięcia, do czego funkcje wyrażeń regularnych nie są przystosowane. Musisz zrobić własną funkcję robiącą to wszystko. Przydają Ci się:
preg_replace, stripos, ale jest to jak najbardziej wykonalne i łatwe jeśli się zastanowisz jak użyć podanych przeze mnie funkcji smile.gif
tvister
Przychodzi mi do głowy improwizacja kombinacją preg_split i preg_grep.
thek
Zarzucę jak ja to widzę algorytmem smile.gif
1. Wykonaj w pętli:
a) znajdź w stringu pozycję </a>
cool.gif dodaj do znalezionej pozycji długość </a>
c) wykonaj tyle razy ile masz w limicie linków.
2. Używając wyrażeń regularnych w pozostałej reszcie usuń znaczniki A wraz z atrybutami, zostawiając treść.

Ale można prościej smile.gif
1. Podziel string na tablicę używając jako delimitera </a> i dając jej maksymalnie LIMIT+1 elementów
2. Jeśli wynikowa tablica ma mniej niż LIMIT+1 elementów zostaw w spokoju.
3. Z ostatniego elementu tablicy usuń wszystkie A z atrybutami, pozostawiając jednak treść pomiędzy tagami A
4. Scal tablicę używając do łączenia </a>

Drugi choć wydaje się dłuższy powinien być szybszy i prostszy w implementacji smile.gif
zegarek84
preg_replace_callback - użyj tej słusznej funkcji, wyrażenie regularne daj uniwersalne na cały link, jako callback zastosuj funkcję "sterownik" która musisz napisac (najlepiej obiektowo) i musisz zliczać wystąpienia danego wyrażenia, a potem wewnątrz zdecydować co chcesz zrobić/zwrócić bądź przekierować na inną funkcję - masz drobne przykłady w manualu tam gdzie podałem link

pozdro...

ps.
niektórzy słabo szukają więc od razu daję jeszcze link (choć jest on też na podanej wyżej stronie):
callback - masz tam przykłady wywoływania funkcji, także obiektowych - jako, że nie jestem zwolenikiem klas statycznych choć czasem się przydają od razu zacytuję:
Cytat
// Type 3: Object method call
$obj = new MyClass();
call_user_func(array($obj, 'myCallbackMethod'));

thek
Zegarek... Też o tym myślałem, ale gdzie przechowasz licznik tego callbacka? Jakaś zmienna statyczna? Bo przecież po wyjściu ze sterownika licznik zginie. Można kombinować z opisanym w komentarzach delayed curryingiem (opóźnionym dołączaniem?), ale moim zdaniem zastosowanie prostszych funkcji powinno dać lepszy rezultat.
Łatwiej zrobić explode, if, preg_replace na ostatnim elemencie i implode, niż kombinować jak napisać callback dla preg_replace_callback by licznik był zmienna globalną dla callbacka smile.gif
zegarek84
podałem przykąłd jak użyć callback do obiektów bez zmiennych statycznych/globalnych ;p - obiektowo najłatwiej napisać sterownik (gdzie to tylko zestaw funkcji z if else...), używając funkcji preg_replace_callback text przeszuka tylko raz...

co prawda nie z linkami ale pisałem coś bardziej złożonego raz i jest to świetna sprawa - preg_replace_callback wypada też deczko szybciej niż sam preg_replace jeśli nic się nie robi - pisze o tym w manualu (choć dawno to czytałem i nie pamiętam gdzie - podejrzewam, że na stronie funkcji winksmiley.jpg )

ps.

preg_replace_callback przekazuje do funkcji callback bodajże tylko jeden parametr - znalazłem jeden swój skrypcik... co ważniejsze ten parametr jest tablicą - tak więc pisząc funkcję callback niech wewnątrz niej zrobi print_r($wejsciowa_zmienna); by zobaczyć gdzie co siedzi - zależy to od skonstruowanego wyrażenia regularnego (czyli w zasadzie tak jak w preg_match)

poza tym inne dosyć fajne rozwiązanie ale na starcie jeśli nigdy tym się nie "bawił" jest przeszukanie tego tekstu przez DOM - ale na razie to rozwiązanie odradzam jeśli nigdy się nim nie bawił gdyż zrozumieć Mu go będzie ciężej winksmiley.jpg
thek
Jeśli nie bazujemy na żadnym ograniczaniu to owszem, callback może być użyty, ale do zwykłego usuwania tagu określonego - przerost formy nad treścią winksmiley.jpg Sam preg_replace to potrafi. Zauważ jednak, że funkcja wywoływana jest za każdym znalezieniem elementu pasującego do wzorca. Musi więc siła rzeczy wiedzieć ile razy została już wywołana, a to bez jakiejś zmiennej statycznej czy globalnej jest nie do osiągnięcia, bo niby jak? Albo więc musi się odnosić do jakiejś statycznej, albo licznik wrzucasz jej jako parametr w argumentach. Oczywiście parametr musi być referencją, bo musisz go inkrementować wewnątrz funkcji i zwykłe wstawienie zmiennej nic nie da, bo utworzy ewentualnie kopię lokalną tej zmiennej, zapominaną po zakończeniu funkcji. Co będzie ostatecznie lepsze? Zależy od ilości i umiejscowienia linków w tekście. W obu ostatecznie jest używana_preg_replace (przypadek gdy osiągnięto tablicę LIMIT+1 elementową) albo wersja callbackowana i różnice nie powinny być ogromne. IMHO jednak dla ilości linków do LIMIT-1 szybsze będzie explode, if, implode niż preg_match_callback. Od LIMIT powinno być to zbliżone.
zegarek84
Cytat(thek @ 8.12.2009, 13:25:16 ) *
...Zauważ jednak, że funkcja wywoływana jest za każdym znalezieniem elementu pasującego do wzorca. Musi więc siła rzeczy wiedzieć ile razy została już wywołana, a to bez jakiejś zmiennej statycznej czy globalnej jest nie do osiągnięcia, bo niby jak? Albo więc musi się odnosić do jakiejś statycznej, albo licznik wrzucasz jej jako parametr w argumentach. Oczywiście parametr musi być referencją, bo musisz go inkrementować wewnątrz funkcji i zwykłe wstawienie zmiennej nic nie da, bo utworzy ewentualnie kopię lokalną tej zmiennej, zapominaną po zakończeniu funkcji...

przyznaj się szczerze, że nie czytałeś tego co pisałem ;p - zmienne globalne questionmark.gif , zmienne statyczne questionmark.gif parametr w argumentach questionmark.gif - tfu tfu - nie po to specjalnie jakby nie mógł doczytać do linków podawałem link do callback i przykład użycia obiektu normalnego - tzn nie statycznego ;p
Cytat(thek @ 8.12.2009, 13:25:16 ) *
Sam preg_replace to potrafi...

sam str_replace to potrafi - wyrażenia regularne questionmark.gif - czyż nie przerost formy nad treścią questionmark.gif tongue.gif - wszystko zależy od logiki aplikacji...
Cytat(thek @ 8.12.2009, 13:25:16 ) *
....W obu ostatecznie jest używana_preg_replace (przypadek gdy osiągnięto tablicę LIMIT+1 elementową) albo wersja callbackowana i różnice nie powinny być ogromne. IMHO jednak dla ilości linków do LIMIT-1 szybsze będzie explode, if, implode niż preg_match_callback. Od LIMIT powinno być to zbliżone.

po primo to pisałem o funkcji preg_replace_callback a nie o preg_match_callback ;P , poza tym akurat czym mniejszy limit tym więcej do zamieniania ;P - i raczej rzekłbym , że jest odwrotnie (aczkolwiek nigdzie nie pisałem, że tak będzie najszybciej - tak będzie najwygodniej) - skoro tak piszesz to sądzę, że zrobiłeś jakieś testy questionmark.gif haha.gif
barat
Dla potomnych będzie - działa biggrin.gif

  1. function zostaw_linki($tekst, $ile = 0)
  2. {
  3. preg_match_all('/<a\s(.*?)>(.*?)<\\/a>/i', $tekst, $linki);
  4. return strrev(preg_replace('/>a\\/<(.*?)>(.*?)\sa</i', '$1', strrev($tekst), (count($linki[0]) - intval($ile)) < 0 ? 0 : count($linki[0]) - intval($ile)));
  5. }
  6. $string = 'Lorem ipsum dolor sit <a href="#">amet</a>, consectetur adipiscing elit. Proin <a href="#">amet</a> elementum odio eget mauris ultricies vulputate. Suspendisse scelerisque vulputate risus ac lobortis. <a href="#">Aenean</a> euismod urna at libero vehicula non sollicitudin elit luctus. Aliquam ultricies nisi ac sapien tempus imperdiet sit amet ut nulla. Proin aliquam blandit libero eu ornare. Suspendisse a erat ligula. Phasellus ultricies odio nec metus dictum eget luctus augue interdum. Morbi at turpis libero, imperdiet iaculis sapien. In sed sapien eget turpis semper imperdiet. <a href="#">Nulla</a> iaculis blandit lorem, eget laoreet mauris euismod sed. Vivamus hendrerit euismod tellus, in adipiscing lectus eleifend in. <a href="#">Aliquam</a> imperdiet placerat orci ac ultrices. Curabitur eu sem tortor, at dapibus dolor.';
  7. echo '<hr />';
  8. echo 'String startowy:';
  9. echo '<hr />';
  10. echo $string;
  11. echo '<hr />';
  12. echo 'zostaw_linki(0)';
  13. echo '<hr />';
  14. echo zostaw_linki($string, 0);
  15. echo '<hr />';
  16. echo 'zostaw_linki(2)';
  17. echo '<hr />';
  18. echo zostaw_linki($string, 2);
  19. echo '<hr />';
  20. echo 'zostaw_linki(4)';
  21. echo '<hr />';
  22. echo zostaw_linki($string, 4);


Pomysł fajny - nie mój, odpowiedź na innym forum smile.gif
zegarek84
tyle, że dając odrazu gotowca nic się nie nauczysz ;P - na tym forum pomagamy a nie dajemy gotowce

PS - PORÓWNAJ SZYBKOŚĆ:

  1. class linki {
  2. private $limit=0;
  3. private $where=0;
  4. private $text;
  5. public function __construct($text) {
  6. $this->text=$text;}
  7. public function setCountLink($i) {
  8. $this->where=0;$this->limit=$i;return $this;}
  9. public function showText() {
  10. return preg_replace_callback('/<a\s[^>]{8,}>(.+?)<\/a>/is',array($this,'replace'), $this->text);}
  11. private function replace($vars) {
  12. return (++$this->where <= $this->limit)?$vars[0]:$vars[1];}
  13. }
  14.  
  15. $string = 'Lorem ipsum dolor sit <a href="#">amet</a>, consectetur adipiscing elit. Proin <a href="#">amet</a> elementum odio eget mauris ultricies vulputate. Suspendisse scelerisque vulputate risus ac lobortis. <a href="#">Aenean</a> euismod urna at libero vehicula non sollicitudin elit luctus. Aliquam ultricies nisi ac sapien tempus imperdiet sit amet ut nulla. Proin aliquam blandit libero eu ornare. Suspendisse a erat ligula. Phasellus ultricies odio nec metus dictum eget luctus augue interdum. Morbi at turpis libero, imperdiet iaculis sapien. In sed sapien eget turpis semper imperdiet. <a href="#">Nulla</a> iaculis blandit lorem, eget laoreet mauris euismod sed. Vivamus hendrerit euismod tellus, in adipiscing lectus eleifend in. <a href="#">Aliquam</a> imperdiet placerat orci ac ultrices. Curabitur eu sem tortor, at dapibus dolor.';
  16.  
  17. $textObject=new linki($string);
  18.  
  19. echo 'String startowy:';
  20. echo '<hr />';
  21. echo $string;
  22. echo '<hr />';
  23.  
  24. echo '$textObject->showText() === $textObject->setCountLink(0)->showText()';
  25. echo '<br /><br />';
  26. echo $textObject->showText();
  27. echo '<hr />';
  28.  
  29. echo '$textObject->setCountLink(2)->showText()';
  30. echo '<br /><br />';
  31. echo $textObject->setCountLink(2)->showText();
  32. echo '<hr />';
  33.  
  34. echo '$textObject->setCountLink(4)->showText()';
  35. echo '<br /><br />';
  36. echo $textObject->setCountLink(4)->showText();
barat
EDIT:
=======================

Ok ... jednak już wszystko OK smile.gif

=======================
Postanowiłem się "pobawić" tym co napisałeś.
Chciałem zamienić Twoje wyrażenie regularne na takie, które wyłapuje tylko linki zewnętrzne + nie będące z konkretnych domen np domena.pl, domena1.com
Czyli:

* http://www.wp.pl - wyczuj
* http://wp.pl - wyczuj
* www.wp.pl - wyczuj
* /katalog/plik.ext - ignoruj
* http://domena.pl -ignoruj
* http://www.domena.pl -ignoruj
* http://www.domena.pl/kontroler/metoda/ -ignoruj
* www.domena.pl -ignoruj

Wyszedł mi taki potworek:
Kod
<a[^\>]+href="(http://www\.|http://|www\.)(?!((www\.)?(domena\.pl|domena1\.com)))[^"]+"([^\>]+)?>([^\<]+)</a>


Wklepanie tego w preg_match_all działa bez problemu.

  1. $str = <<<TEST
  2. Welcome to RegExr 0.3b, an intuitive tool for learning, writing, and testing Regular Expressions. Key features include:
  3.  
  4. * real time results: shows results as you type
  5. * code hinting: roll over your expression to see info on specific elements
  6. * detailed results: roll over a match to see details & view group info below
  7. <a href="http://www.wp.pl" title="aa">aaa</a> fdsg </a>
  8. * built in regex guide: double click entries to insert them into your expression
  9. * online & desktop: <a title="ar" href="http://regexr.com" name="saf">bb</a> or download the desktop version for Mac, Windows, or Linux
  10. * save your expressions: My Saved expressions are saved locally
  11. * share and rate expressions: search Community expressions and share your own <a href="http://www.szkolenia24h.pl/aaa/ssss">wew1</a>
  12.  
  13. Built by gskinner.com with Flex 3 [adobe.com/go/flex] and Spelling Plus <a href="http://szkolenia24h.pl/aaa/ssss">wew2</a> Library for text highlighting [gskinner.com/products/spl].
  14. <a href="/aaa/ssss">wew3</a>
  15. There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model<a href="www.wp.pl" title="asd">sentence structures</a>, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.
  16. There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need<a href="http://szkolenia24h.pl/aaa/ssss">wew2</a> to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem <a title="ar" href="http://regexr.com" name="saf">bb</a> Ipsum is freepetition, injected humour, or <a href="http://aaa.pl">noncg-haracteristic</a> words etc.
  17. There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable.<a href="http://www.kursy24h.pl">aa</a> If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as <a href="http://www.wp.pl" title="asd">noncg-haracteristic</a>necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.
  18. TEST;
  19.  
  20. preg_match_all('/<a[^\>]+href="(http:\/\/www\.|http:\/\/|www\.)(?!((www\.)?(szkolenia24h\.pl|kursy24h\.pl)))[^"]+"([^\>]+)?>([^\<]+)<\/a>/is', $strt, $linki);
  21.  
  22. print_r($linki);


jednak użycie tej regułki w Twojej klasie (zamieniając $vars[0]:$vars[1]; na $vars[0]:$vars[6]winksmiley.jpg nie działa ... zgłupiałem :/
Teoretycznie powinno zachować się tak samo... różnica między wyrażeniami jest taka, że moje zwraca 7 elementów zamiast dwóch ...

  1. (
  2. [0] => Array
  3. (
  4. [0] => <a href="http://www.wp.pl" title="aa">aaa</a>
  5. [1] => <a title="ar" href="http://regexr.com" name="saf">bb</a>
  6. [2] => <a href="www.wp.pl" title="asd">sentence structures</a>
  7. [3] => <a title="ar" href="http://regexr.com" name="saf">bb</a>
  8. [4] => <a href="http://aaa.pl">noncg-haracteristic</a>
  9. [5] => <a href="http://www.wp.pl" title="asd">noncg-haracteristic</a>
  10. )
  11.  
  12. [1] => Array
  13. (
  14. [0] => [url="http://www"]http://www[/url].
  15. [1] => http://
  16. [2] => www.
  17. [3] => http://
  18. [4] => http://
  19. [5] => [url="http://www"]http://www[/url].
  20. )
  21.  
  22. [2] => Array
  23. (
  24. [0] =>
  25. [1] =>
  26. [2] =>
  27. [3] =>
  28. [4] =>
  29. [5] =>
  30. )
  31.  
  32. [3] => Array
  33. (
  34. [0] =>
  35. [1] =>
  36. [2] =>
  37. [3] =>
  38. [4] =>
  39. [5] =>
  40. )
  41.  
  42. [4] => Array
  43. (
  44. [0] =>
  45. [1] =>
  46. [2] =>
  47. [3] =>
  48. [4] =>
  49. [5] =>
  50. )
  51.  
  52. [5] => Array
  53. (
  54. [0] => title="aa"
  55. [1] => name="saf"
  56. [2] => title="asd"
  57. [3] => name="saf"
  58. [4] =>
  59. [5] => title="asd"
  60. )
  61.  
  62. [6] => Array
  63. (
  64. [0] => aaa
  65. [1] => bb
  66. [2] => sentence structures
  67. [3] => bb
  68. [4] => noncg-haracteristic
  69. [5] => noncg-haracteristic
  70. )
  71. )


vs

  1. (
  2. [0] => Array
  3. (
  4. [0] => <a href="http://www.wp.pl" title="aa">aaa</a>
  5. [1] => <a title="ar" href="http://regexr.com" name="saf">bb</a>
  6. [2] => <a href="http://www.szkolenia24h.pl/aaa/ssss">wew1</a>
  7. [3] => <a href="http://szkolenia24h.pl/aaa/ssss">wew2</a>
  8. [4] => <a href="/aaa/ssss">wew3</a>
  9. [5] => <a href="www.wp.pl" title="asd">sentence structures</a>
  10. [6] => <a href="http://szkolenia24h.pl/aaa/ssss">wew2</a>
  11. [7] => <a title="ar" href="http://regexr.com" name="saf">bb</a>
  12. [8] => <a href="http://aaa.pl">noncg-haracteristic</a>
  13. [9] => <a href="http://www.kursy24h.pl">aa</a>
  14. [10] => <a href="http://www.wp.pl" title="asd">noncg-haracteristic</a>
  15. )
  16.  
  17. [1] => Array
  18. (
  19. [0] => aaa
  20. [1] => bb
  21. [2] => wew1
  22. [3] => wew2
  23. [4] => wew3
  24. [5] => sentence structures
  25. [6] => wew2
  26. [7] => bb
  27. [8] => noncg-haracteristic
  28. [9] => aa
  29. [10] => noncg-haracteristic
  30. )
  31.  
  32. )
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.