how to chop of a text in a certain length with php? - php

i wanna get some field values from database and present them on html.
but some of them are longer than the div width so i wanted to chop if of and add 3 dots after them if they are longer than lets say 30 characthers.
windows vs mac os x-> windows vs m...
threads about windows vista -> threads about win...
how can i do that?

If you need to perform this kind of functionality more than once, consider this function:
function truncate($string, $limit, $break = '.', $pad = '...')
{
// return with no change if string is shorter than $limit
if(strlen($string) <= $limit) return $string;
// is $break present between $limit and the end of the string?
if(false !== ($breakpoint = strpos($string, $break, $limit)))
{
if($breakpoint < strlen($string) - 1)
{
$string = substr($string, 0, $breakpoint) . $pad;
}
}
return $string;
}
Usage:
echo truncate($string, 30);

Judging by your examples you don't seem to care about preserving words, so here it is:
if (strlen($str) > 30)
{
echo substr($str, 0, 30) . '...';
}

If you use Smarty, you can use the truncate modifier.
{myLongText|truncate:30:'...':true}
BTW, such kind of function should exist in any decent template engine.

Check out wordwrap(), that should be what you're looking for.

Related

How to check if punctuation exists in a given amount of characters and if it does cut the text there - PHP?

I wish to check if there is a fullstop or question mark (i.e two needles) in the first 100 characters of an excerpt. If there is one then the excerpt would be cut off at the full stop, else it would be cut off at 100 characters.
Now I have the following which checks the position of the the needle: if it is under 100 characters then the excerpt is cut there, else the excerpt is cut at 100 characters with "...". The issue I have is that I wish to check for multiple needles, I have tried various methods such as looping and preg_match, but can get neither to work. Very new to php, so any pointers are much appreciated.
<?php
ob_start();
the_excerpt();
$excerpt = ob_get_clean();
$excerpt = preg_replace('/<.*?>/', '', $excerpt);;
?>
<?php
$needle = '.';
$position = stripos($excerpt, $needle);
if($position < 100) {
$offset = $position + 0;
$position2 = stripos ($excerpt, $needle, $offset);
$first_two = substr ($excerpt, 0, $position2);
echo $first_two . '.';
}
else {
$cut = substr( $excerpt, 0, 100 );
$excerpt = substr( $cut, 0, strrpos( $cut, ' ' ) ) . '...';
echo $excerpt;
}
?>
I would try something like this. It needs lesser loops, and makes it possible to easily add needles. Cut the string at 100 characters right away. It is not going to be longer, there is no need to search the whole string.
<?php
ob_start();
the_excerpt();
$excerpt = ob_get_clean();
$maxLength = 100;
$excerpt = substr(preg_replace('/<.*?>/', '', $excerpt), 0, $maxLength);
$needles = ['.','?'];
$offset = min($maxLength, strlen($excerpt));
$lnf = $offset; // to store last needle found
for ($index = 0; $index < $offset; $index++) {
if (in_array($excerpt[$index], $needles)) {
$lnf = $index + 1;
}
}
$excerpt = substr($excerpt, 0, $lnf);
?>
You want to have the last occurrence of needle or all occurrences?
// dont use the buffering, there is also a get_the_excerpt function
$excerpt = get_the_excerpt();
$needle = '.';
// use strrpos to get the last occurrence or strpos to get the first
$needle_pos = strrpos(substr($excerpt,0,100), $needle);
if($needle_pos !== false) {
// we have a needle, so lets change the excerpt
$excerpt = substr($excerpt, 0, $needle_pos);
}
echo substr($excerpt, 0, 100) . '..'; // when its already cut it is less then 100 chars
Or even better, you could be looking into using filters. A filter is something from Wordpress. Everywhere in the Wordpress code there are function calls to apply_filters([somename], $somevariable). It will execute all functions connected to that [somename] tag by providing the $somevariable and storing the return value there. You can add your own filters by using add_filter([somename], [some callback]). E.g.:
add_filter('the_excerpt', cutoffExcerpt);
function cutoffExcerpt($excerpt) {
$needle = '.';
// use strrpos to get the last occurrence or strpos to get the first
$needle_pos = strrpos(substr($excerpt,0,100), $needle);
if($needle_pos !== false) {
// we have a needle, so lets change the excerpt
$excerpt = substr($excerpt, 0, $needle_pos);
}
return substr($excerpt, 0, 100) . '..'; // when its already cut it is less then 100 chars
}
You should add this to your functions.php file in your theme (or create that file if you don't have it yet). Now when you use the_excerpt() in your template it will be cut off at the needle without any extra hassle.
This is very easy with a regex.
Match all the needles ., !, ?, , and ;. Of course you can add more ore remove.
PREG_OFFSET_CAPTURE flag gives the position where it was found
iterate thru the results backwards
take that one with <= 100 and cut from there.
$lorem = 'Shields up, sub-light advice! The pathway dies mind like a solid klingon. The space is tightly biological. C-beams walk on beauty at earth!';
$matches = [];
preg_match_all('/\.|\!|\?|,|;/', $lorem, $matches, PREG_OFFSET_CAPTURE);
for($i = count($matches[0])-1; $i >= 0; $i--) {
echo $matches[0][$i][1], PHP_EOL;
if($matches[0][$i][1] <= 100) {
echo "cutting at ", $matches[0][$i][1], PHP_EOL;
$excerpt = substr($lorem, 0, $matches[0][$i][1] + 1) . ' ...';
break;
}
}
echo $excerpt, PHP_EOL;
Outputting
138
105
72
cutting at 72
The final result then is
Shields up, sub-light advice! The pathway dies mind like a solid klingon. ...
Of course you can remove the echoes, they are just for helping to figure out what is happening.
use PHP explode() to brake needles
$excerpt = "The trees, therefore, must be such old and primitive techniques that they thought nothing of them, deeming them so inconsequential that even savages like us would know of them and not be suspicious. At that, they probably didn't have too much time after they detected us orbiting and intending to land. And if that were true, there could be only one place where their civilization was hidden.";
$needles=['.','?',','];
$str=[$excerpt];
for($i=0;$i<sizeof($needles);$i++) {
if($i!=0){
$str[0] .= $needles[$i-1];
}
$str=explode($needles[$i],$str[0]);
}
if(strlen($str[0])<100){
$excerpt=$str[0];
}
else{
$excerpt=substr($excerpt,0,100);
$excerpt .= "...";
}
echo $excerpt;

PHP: trim word OR part of it from begining/end of string

I need to trim words from begining and end of string. Problem is, sometimes the words can be abbreviated ie. only first three letters (followed by dot).
I tried hard to find suitable regular expression. Basicaly I need to chatch three or more initial characters up to length of replacement, but I cannot find regular expression, that will match variable length and will keep order of characters.
For example, if I need to trim 'insurance' from sentence 'insur. companies are rich', then pattern \^[insurance]{3,9}\ comes to my mind, but this pattern will also catch words like 'sensace', because order of characters (and their occurance) inside [] is not important for regexp.
Also, at end of string, I need remove serial-numbers, that are abbreviated from beginig - say 'XK-25F14' is sometimes presented as '25F14'. So I decided to go purely with character by character comparison.
Therefore I end with following php function
function trimWords($s, $dirt, $case_insensitive = false, $reverse = true)
{
$pos = 0;
$func = $case_insensitive ? 'strncasecmp' : 'strncmp';
// Get number of initial characters, that match in both strings
while ($func($s, $dirt, $pos + 1) === 0)
$pos++;
// If more than 2 initial characters match, then remove the match
if ($pos > 2)
$s = substr($s, $pos);
// Reverse $s and $dirt so it will trim from the end of string
$s = strrev($s);
if ($reverse)
return trimWords($s, strrev($dirt), $case_insensitive, false);
// After second run return back-reversed string
return trim($s, ' .-');
}
I'm happy with this function, but it has one drawback. It trims only one occurence of word. How to make it trim more occurances, i.e. remove both 'insurance ' from 'Insurance insur. companies'.
And I'm also curious, it realy does not exists such regular expression, that will match variable length and will respect order of characters in pattern?
Final solution
Thanks to mrhobo I have ended with function based on regular expression. This function can be easily improved and shall also be the most efficient for this task.
I have modified my previous function and it is two times quicker than regexp, but it can remove only one word per single run, so to be able to remove word from begin and end, it has to runs itself twice and performance is same as regexp and to remove more than one occurance of word, it has to runs itself multiple times, which will then be more and more slower.
The final function goes like this.
function trimWords($string, $word, $case_insensitive = false, $min_abbrv = 3)
{
$exc = substr($word, $min_abbrv);
$pat = null;
$i = strlen($exc);
while ($i--)
$pat = '(?>'.preg_quote($exc[$i], '#').$pat.')?';
$pat = substr($word, 0, $min_abbrv).$pat;
$pat = '#(?<begin>^)?(?:\W*\b'.$pat.'\b\W*)+(?(begin)|$)#';
if ($case_insensitive)
$pat .= 'i';
return preg_replace($pat, '', $string);
}
NOTE: with this function, it does not matter, if abbreviation ends with dot or not, it wipes out any shorter form of word and also removes all nonword characters around the word.
EDIT: I just tried create replace pattern like insu(r|ra|ran|ranc|rance) and function with atomic groups is faster by ~30% and with longer words it could be possibly even more efficient.
Matching a word and all possible abbreviations from the nth letter isn't quite an easy task in regex.
Here is how I would do it for the word insurance from the 4th letter:
insu(?>r(?>a(?>n(?>c(?>(?<last>e))?)?)?)?)?(?(last)|\.)
http://regex101.com/r/aL2gV4
It works by using atomic groups to force the regex engine as far as possible forward past the last 'rance' letters using the nested pattern (?>a(?>b)?)?. If the last letter letter is matched we're not dealing with an abbreviation thus no dot is required, otherwise the dot is required. This is coded by (?(last)|\.).
To trim, I would create a function to build the above regex for an abbreviation. Then you can write a while loop that replaces each of the abbreviation regexes with empty space until there are no more matches.
Non regex version
Here is my non regex version that removes multiple words and abbreviated words from a string:
function trimWords($str, $word, $min_abbrv, $case_insensitive = false) {
$len = 0;
$word_len = strlen($word);
$strlen = strlen($str);
$cmp = $case_insensitive ? strncasecmp : strncmp;
for ($i = 0; $i < $strlen; $i++) {
if ($cmp($str[$i], $word[$len], $i) == 0) {
$len++;
} else if ($len > 0) {
if ($len == $word_len || ($len >= $min_abbrv && ($dot = $str[$i] == '.'))) {
$i -= $len;
$len += $dot;
$str = substr($str, 0, $i) . substr($str, $i+$len);
$strlen = strlen($str);
$dot = 0;
}
$len = 0;
}
}
return $str;
}
Example:
$string = 'ins. <- "ins." / insu. insuranc. insurance / insurance. <- "."';
echo trimWords($string, 'insurance', 4);
Output is:
ins. <- "ins." / / . <- "."
I wrote function that constructs regular expression pattern according to mrhobo and also simple test and benchmarked it against my function with pure PHP string comparison.
Here is the code:
$string = 'Insur. companies are nasty rich';
$dirt = 'insurance';
$cycles = 500000;
$start = microtime(true);
$i = $cycles;
while ($i) {
$i--;
regexpStyle($string, $dirt, true);
}
$stop = microtime(true);
$i = $cycles;
while ($i) {
$i--;
trimWords($string, $dirt, true);
}
$end = microtime(true);
$res1 = $stop - $start;
$res2 = $end - $stop;
$winner = $res1 < $res2 ? '<<<' : '>>>';
echo 'regexp: '.$res1.' '.$winner.' string operations: '.$res2;
function trimWords($s, $dirt, $case_insensitive = false, $reverse = true)
{
$pos = 0;
$func = $case_insensitive ? 'strncasecmp' : 'strncmp';
// Get number of initial characters, that match in both strings
while ($func($s, $dirt, $pos + 1) === 0)
$pos++;
// If more than 2 initial characters match, then remove the match
if ($pos > 2)
$s = substr($s, $pos);
// After second run return back-reversed string
return trim($s, ' .-');
}
function regexpStyle($s, $dirt, $case_insensitive, $min_abbrev = 3)
{
$ss = substr($dirt, $min_abbrev);
$arr = str_split($ss);
$patt = '(?>(?<last>'.array_pop($arr).'))?';
$i = count($arr);
while ($i)
$patt = '(?>'.$arr[--$i].$patt.')?';
$patt = '#^'.substr($dirt, 0, $min_abbrev).$patt.'(?(last)|\.)#';
$patt .= $case_insensitive ? 'i' : null;
return trim(preg_replace($patt, '', $s));
}
and the winner is... moment of silence... it is...
a draw
regexp: 8.5169589519501 >>> string operations: 8.0951890945435
but I have strong feeling that regexp approach could be better utilized.

php improper comparison behavior

$string = '540';
if (strlen ($string >= 34)){
print_r((substr($string, 0, 30) . "..."));
} else {
print_r(($string));
}
If $string is longer than 34 characters it should be appended with a "...", otherwise it should just print the string.
I think what's happening is that the interpreter is assuming the string is a number when it does the comparison.
It also has the same hiccup if I change $string to
$string = '540 rocks !'
Why is this?
Should be:
if (strlen($string) >= 34)) {
Not
if ($string >= 34)){
If the test you want to do is on the string length, just change this line:
if ($string >= 34)){
into this:
if (strlen($string) >= 34)){

How to extract substring by start-index and end-index?

$str = 'HelloWorld';
$sub = substr($str, 3, 5);
echo $sub; // prints "loWor"
I know that substr() takes the first parameter, 2nd parameter is start index, while 3rd parameter is substring length to extract. What I need is to extract substring by startIndex and endIndex. What I need is something like this:
$str = 'HelloWorld';
$sub = my_substr_function($str, 3, 5);
echo $sub; // prints "lo"
Is there a function that does that in php? Or can you help me with a workaround solution, please?
It's just math
$sub = substr($str, 3, 5 - 3);
The length is the end minus the start.
function my_substr_function($str, $start, $end)
{
return substr($str, $start, $end - $start);
}
If you need to have it multibyte safe (i.e. for chinese characters, ...) use the mb_substr function:
function my_substr_function($str, $start, $end)
{
return mb_substr($str, $start, $end - $start);
}
Just subtract the start index from the end index and you have the length the function wants.
$start_index = 3;
$end_index = 5;
$sub = substr($str, $start_index, $end_index - $start_index);
You can just use a negative value on the third parameter:
echo substr('HelloWorld', 3, -5);
// will print "lo"
If length is given and is negative, then that many characters will be omitted from the end of string (after the start position has been calculated when a start is negative).
As stated at the substr documentation.
Not exactly...
If we have a start index as 0, and we want JUST the first char, it becomes difficult as this will not output what you want. So if your code is requiring an $end_index:
// We want just the first char only.
$start_index = 0;
$end_index = 0;
echo $str[$end_index - $start_index]; // One way... or...
if($end_index == 0) ++$end_index;
$sub = substr($str, $start_index, $end_index - $start_index);
echo $sub; // The other way.

Cutting down a length of a PHP string and inserting an ellipses

I want to turn a long string like reallyreallyreallyreallyreallylongfilename into something like reallyreallyre...yreallyreally.
Basically, find the middle of the string and replace everything there until the length of the string is < 30 characters including an ellipses to signify there has been parts of the string replaced.
This is my code where I have tried this:
function cutString($input, $maxLen = 30)
{
if(strlen($input) < $maxLen)
{
return $input;
}
$midPoint = floor(strlen($input) / 2);
$startPoint = $midPoint - 1;
return substr_replace($input, '...', $startPoint, 3);
}
It finds the center of the string and replaces a character either side with . but the thing is I can't work out how to make it cut it down to 30 characters, or whatever $maxLen is.
Hopefully you understand my question, I don't think I did a very good job at explaining it 8)
Thanks.
How about:
if (strlen($input) > $maxLen) {
$characters = floor($maxLen / 2);
return substr($input, 0, $characters) . '...' . substr($input, -1 * $characters);
}

Categories