Jump to content
BulForum.com

Regular Expressions - регулярни изрази


tedy

Recommended Posts

Понеже често ги ползвам в Perl варианта им като по-мощен, а и знам, че има доста хора тук, които отбират от тях (макар че ги ползвам, не съм някакъв супер-майстор все пак), реших да направя темичка за тях, понеже са адски мощен инструмент за манипулация на стрингове.

Аз лично ще давам примери на PHP, понеже там ги ползвам основно. Но то е само въпрос до конкретен синтаксис, изразите са си изрази.

 

Та ми се наложи примерно сега да правя следната манипулация с HTML текст.

Админ на сайт си въвежда текстове в WYSIWYG едитор, резултатът е HTML.

А задачата е този текст да се ограничи като брой символи, които излизат на екрана на някои места, където мястото е ограничено и обикновено има линк Read more... за страница с целия текст. И ограничения текст завършва с 3 точки, показващи, че не е пълен.

За обикновен текст е повече от лесно и без regex. В случая имаме всякакви тагове, и т.н. и не е толкова тривиално да се ограничи само текста до брой символи предвид че искаме всички тагове да не ги бараме за да не се получи недовършен таг.

Обикновено такива проблемчета си ги решавам сам, понеже от една страна така научавам доста за езика, за дадена тенология и т.н. Освен ако ще отнеме дни за нещо просто, разбира се.

 

Реших да действам така. Разделям стринга в масив с preg_split() по таговете. Така в масив имам елемент-текст, следван от елемент-последователни-тагове-без-текст, следван от елемент-текст и т.н. Работя по елементите, които са само текст и отзад почвам да ги намалявам докато получа желания ограничен брой символи. Накрая съединявам отново всички елементи и резултатът е готов.

Мисля, че се получи добре.

 

Въпросът беше в регекса, който ще цепи сорс стринга.

Първоначалният вариант беше

 

$elements = preg_split('/(<.+?>)/', $s, -1, PREG_SPLIT_DELIM_CAPTURE + PREG_SPLIT_NO_EMPTY);

Флаговете показват да не се връщат празни елементи (между таговете се получават), и да върне и мач-натия в скобите израз, за да може да се включи в крайняи стринг разбира се.

Това обаче връщаше последователни тагове като <strong><i>text</i></strong> като отделни елементи, в случая 5 елемента, като очевидно имаме последователни елементи-тагове. И това върши работа, но със следния вариант всички последователни тагове отиват в един елемент както трябва:

 

$elements = preg_split('/(?<!>)(<.+?>)(?!<)/', $s, -1, PREG_SPLIT_DELIM_CAPTURE + PREG_SPLIT_NO_EMPTY);

 

И въпросът е дали има и по-лесен начин за регекс който да хване таговете, около които няма други тагове (това е целта, щото около група тагове имаме текст някакъв), без използването на lookbehind/lookahead assertions? последните не съм сигурен дали са достъпни в POSIX синтаксиса, който не ползвам, но все пак дори да са достъпни, с цел намирането на най-добрия метод може да се окаже полезно. А и POSIX функциите в PHP май не могат да върнат в масива И мачнатите хави, което в случая ми е нужно. Знам, че има куп други алгоритми за изпълнението на задачата, като завъртане на цикъл след сплит-а по регекса, дори да не се върнат мачнатите елементи, но... така мисля е по-кратко.

 

Edit: ето и пълната функция:

function LimitHTMLTextLength($s, $len) {
$elements = preg_split('/(?<!>)(<.+?>)(?!<)/', $s, -1, PREG_SPLIT_DELIM_CAPTURE + PREG_SPLIT_NO_EMPTY);
$e_count = count($elements);
$i_start = 0;   // we will step only onto text-elements = better performance
if ($elements[0][0]=='<') $i_start = 1;
$slen = 0;
for ($i=$i_start; $i<$e_count; $i+=2) $slen += strlen($elements[$i]);   // get original text length

if ($slen>$len) {
	$i_end = $e_count-1;
	if ($elements[$i_end][0]=='<') $i_end--;
	for ($i=$i_end; $i>=0; $i-=2) {
		$i_len = strlen($elements[$i]);
		if (($slen-$i_len) <= $len) {   // limit this element and break
			$elements[$i] = substr($elements[$i], 0, $len-$slen) . '...';
			break;
		} else {
			$slen -= $i_len;
			$elements[$i] = '';
		}
	}
}

return implode('', $elements);
}

Link to comment
Share on other sites

(<.+?>)+

В случая това не работи с горната функция, понеже е в контекста на разделяне по тоя израз, отделно "хващам" и съдържимото в скобите в израза (последователните тагове), за да го ползвам след това при събирането на елементите в един стринг за да не загубя таговете.

В случая твоят израз го написвам така:

$elements = preg_split('/((<.+?>)+)/', $s, -1, PREG_SPLIT_DELIM_CAPTURE + PREG_SPLIT_NO_EMPTY);

Но външните скоби хващат целия израз, а вътрешните - очевидно дублират последния таг от група (ако има такава), и се добавя последния таг още веднъж. Т.е. резултата от "this is <div style="font-size:11px"><i>both</i></div>" е масив

 

[this is ],

[<div style="font-size:11px"><i>],

[<i>],

[both],

[</i></div>]

[</div>]

 

Не че го мисля толкова, но пък май варианта както е, си е Ок, все пак това е един от най-простите изрази, които ми се е налагало да ползвам. То в по-прости случаи и без тях става.

 

EDIT:

Уф че съм разсеян, разбира се че има опция и за това :), така вече става:

 

$elements = preg_split('/((?:<.+?>)+)/', $s, -1, PREG_SPLIT_DELIM_CAPTURE + PREG_SPLIT_NO_EMPTY);

Мисля, че така е малко по-оптимизирано от първия вариант. 10х.

Link to comment
Share on other sites

  • 3 months later...

не си хванал специалните символи като < > "

т.е. ако имаш стринг: "абв>где"

при теб идва "абв&гt;где"

режем до 5я знак и:

и ти го режеш на "абв"

а би трябвало да е "абв&гt;г"

 

и аз това мисля сега :)

Link to comment
Share on other sites

не си хванал специалните символи като < > "

т.е. ако имаш стринг: "абв>где"

при теб идва "абв&гt;где"

режем до 5я знак и:

и ти го режеш на "абв"

а би трябвало да е "абв&гt;г"

 

и аз това мисля сега :)

Еми то затова тия неща винаги търпят промени след като вендъж е писано по тях :) . Това е стара тема, а тогава май се оказа, че е по-удобно с bbcode едитор станаха нещата.

Горния регекс си важи, единствено за случая със & ентитата ще се барат елементите от масива с текст. Предварително може в тях да се заместят тези entities със съответните символи, да се трункатне както досега, и после изхода разбира се ще се хтмлспециалцхарс-нат отново :) . Или след трункате на съответния елемент да се провери дали не е пресечен някой entity такъв. Тва е бял кахър.

Между другото потресаващо много сайтове виждам още, с utf-8 чарсет, поголовно виждам повредени стрингове, особено в края дето са съкратени с read more... линкове, по не-utf8 съпобразен начин, и в края седят разни ромбчета :) .

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...