- PHP: substr и мощные альтернативы, чтобы вырезать часть строки
- Получаем строку начиная с определенного символа
- Получаем определенное количество символов
- Вырезаем символы с конца
- Получаем несколько последних символов
- Получаем первый символ строки
- Получаем последний символ строки
- Получение подстроки по регулярному выражению
- Проблема при работе с многобайтовыми кодировками.
- Продвинутая работа со строками
- substr_count
- Parameters
- Return Values
- Changelog
- Examples
- See Also
- User Contributed Notes 10 notes
PHP: substr и мощные альтернативы, чтобы вырезать часть строки
Поговорим о том, как обрезать строку в PHP. Функция substr в предназначена для получения части строки. Она выделяет подстроку из переданной строки и возвращает её. Для этого нужно указать строку, порядковый номер символа, начиная с которого нужно вырезать строку, порядковый номер символа, до которого мы верезаем подстроку.
Обратите внимание, что substr неправильно работает с многобайтовыми кодировками, поэтому мы будем использовать mb_substr, которая работает с ними корректно. Об этой проблеме немного ниже.
mb_substr( string $string, int $start, int|null $length = null, string|null $encoding = null ): string
- $string — строка, из которой мы будем вырезать подстроку
- $start — символ, с которого мы будем вырезать подстроку
- $length — сколько символов мы будем вырезать (по умолчанию будем вырезать все оставшиеся)
- $encoding — кодировка
Теперь перейдем к примерам.
Получаем строку начиная с определенного символа
$url = 'https://phpstack.ru/admin/'; $result = mb_substr($url, 8); echo $result; // результат: phpstack.ru/admin/
Мы вырезали первые 8 символов из строки, содержащей URL адрес.
Получаем определенное количество символов
Теперь давайте вырежем еще и «/admin/» в конце.
Мы бы могли сделать это указав количество символов, которое нужно взять, оно равно количеству символов в домене, их 11
$url = 'https://phpstack.ru/admin/'; $result = mb_substr($url, 8, 11); // возьмем 11 символов начиная с 8-го echo $result; // результат: phpstack.ru
Вырезаем символы с конца
Что если мы не знаем количества символов в домене, но знаем что нужно вырезать строку «/admin/», длина которой составляет 7 символов? Иными словами нам нужно вырезать с конца.
В таком случае нужно указать отрицательное число:
$url = 'https://phpstack.ru/admin/'; $result = mb_substr($url, 8, -7); // удаляем 8 символов с начала и 7 символов с конца echo $result; // результат: phpstack.ru
Получаем несколько последних символов
Что если нам нужно вернуть наоборот только 7 последних символов? Тогда код будет следующим:
$url = 'https://phpstack.ru/admin/'; $result = mb_substr($url, -7); // Получаем 7 символов с конца, остальное удаляем echo $result; // результат: /admin/
Получаем первый символ строки
$url = 'https://phpstack.ru/admin/'; $result = mb_substr($url, 0, 1); // Начиная с 0 берем 1 символ echo $result; // результат: h
Получаем последний символ строки
$url = 'https://phpstack.ru/admin/'; $result = mb_substr($url, -1); // начинаем на 1 символ относительно конца echo $result; // результат: /
Получение подстроки по регулярному выражению
Если вам не хватает возможностей substr, возможно вам необходим более мощный инструмент: preg_match — эта функция позволяет получить подстроки по регулярному выражению.
Проблема при работе с многобайтовыми кодировками.
$url = 'привет'; $result1 = mb_substr($url, 3); // удаляем 3 символа с начала $result2 = substr($url, 3); // удаляем 3 символа с начала var_dump($result1); // вет var_dump($result2); // �ивет
Что случилось? Почему в первом случае, где мы использовали mb_substr все сработало хорошо, а во втором случае вернулся какой-то битый символ?
Дело в том, что в UTF-8 кириллица кодируется 2 байтам, вместо одного. substr считает, что символ равен байту и поэтому вырезает 3 байта с начала. Таким образом она вырезала букву «П», и только половину буквы «Р». В общем вы поняли: всегда используйте mb_substr когда работаете с текстом, который потенциально может содержать многобайтовые символы.
Продвинутая работа со строками
Если вы часто работаете со строками, вам пригодится это расширение: symfony/string
С его помощью вы сможете легко вырезать строки. Взгляните на несколько примеров:
// returns a substring which starts at the first argument and has the length of the // second optional argument (negative values have the same meaning as in PHP functions) u('Symfony is great')->slice(0, 7); // 'Symfony' u('Symfony is great')->slice(0, -6); // 'Symfony is' u('Symfony is great')->slice(11); // 'great' u('Symfony is great')->slice(-5); // 'great' // reduces the string to the length given as argument (if it's longer) u('Lorem Ipsum')->truncate(3); // 'Lor' u('Lorem Ipsum')->truncate(80); // 'Lorem Ipsum' // the second argument is the character(s) added when a string is cut // (the total length includes the length of this character(s)) u('Lorem Ipsum')->truncate(8, '…'); // 'Lorem I…' // if the third argument is false, the last word before the cut is kept // even if that generates a string longer than the desired length u('Lorem Ipsum')->truncate(8, '…', false); // 'Lorem Ipsum'
substr_count
substr_count() returns the number of times the needle substring occurs in the haystack string. Please note that needle is case sensitive.
Note:
This function doesn’t count overlapped substrings. See the example below!
Parameters
The substring to search for
The offset where to start counting. If the offset is negative, counting starts from the end of the string.
The maximum length after the specified offset to search for the substring. It outputs a warning if the offset plus the length is greater than the haystack length. A negative length counts from the end of haystack .
Return Values
This function returns an int .
Changelog
Version | Description |
---|---|
8.0.0 | length is nullable now. |
7.1.0 | Support for negative offset s and length s has been added. length may also be 0 now. |
Examples
Example #1 A substr_count() example
$text = ‘This is a test’ ;
echo strlen ( $text ); // 14
?php
echo substr_count ( $text , ‘is’ ); // 2
// the string is reduced to ‘s is a test’, so it prints 1
echo substr_count ( $text , ‘is’ , 3 );
// the text is reduced to ‘s i’, so it prints 0
echo substr_count ( $text , ‘is’ , 3 , 3 );
// generates a warning because 5+10 > 14
echo substr_count ( $text , ‘is’ , 5 , 10 );
// prints only 1, because it doesn’t count overlapped substrings
$text2 = ‘gcdgcdgcd’ ;
echo substr_count ( $text2 , ‘gcdgcd’ );
?>
See Also
- count_chars() — Return information about characters used in a string
- strpos() — Find the position of the first occurrence of a substring in a string
- substr() — Return part of a string
- strstr() — Find the first occurrence of a string
User Contributed Notes 10 notes
It’s worth noting this function is surprisingly fast. I first ran it against a ~500KB string on our web server. It found 6 occurrences of the needle I was looking for in 0.0000 seconds. Yes, it ran faster than microtime() could measure.
Looking to give it a challenge, I then ran it on a Mac laptop from 2010 against a 120.5MB string. For one test needle, it found 2385 occurrences in 0.0266 seconds. Another test needs found 290 occurrences in 0.114 seconds.
Long story short, if you’re wondering whether this function is slowing down your script, the answer is probably not.
Making this case insensitive is easy for anyone who needs this. Simply convert the haystack and the needle to the same case (upper or lower).
To account for the case that jrhodes has pointed out, we can change the line to:
substr_count ( implode( ‘,’, $haystackArray ), $needle );
array (
0 => «mystringth»,
1 => «atislong»
);
Which brings the count for $needle = «that» to 0 again.
substr_count ( implode( $haystackArray ), $needle );
instead of the function described previously, however this has one flaw. For example this array:
array (
0 => «mystringth»,
1 => «atislong»
);
If you are counting «that», the implode version will return 1, but the function previously described will return 0.
Yet another reference to the «cgcgcgcgcgcgc» example posted by «chris at pecoraro dot net»:
Your request can be fulfilled with the Perl compatible regular expressions and their lookahead and lookbehind features.
$number_of_full_pattern = preg_match_all(‘/(cgc)/’, «cgcgcgcgcgcgcg», $chunks);
works like the substr_count function. The variable $number_of_full_pattern has the value 3, because the default behavior of Perl compatible regular expressions is to consume the characters of the string subject that were matched by the (sub)pattern. That is, the pointer will be moved to the end of the matched substring.
But we can use the lookahead feature that disables the moving of the pointer:
$number_of_full_pattern = preg_match_all(‘/(cg(?=c))/’, «cgcgcgcgcgcgcg», $chunks);
In this case the variable $number_of_full_pattern has the value 6.
Firstly a string «cg» will be matched and the pointer will be moved to the end of this string. Then the regular expression looks ahead whether a ‘c’ can be matched. Despite of the occurence of the character ‘c’ the pointer is not moved.
a simple version for an array needle (multiply sub-strings):
function substr_count_array ( $haystack , $needle ) $count = 0 ;
foreach ( $needle as $substring ) $count += substr_count ( $haystack , $substring );
>
return $count ;
>
?>
Unicode example with «case-sensitive» option;
function substr_count_unicode ( $str , $substr , $caseSensitive = true , $offset = 0 , $length = null ) if ( $offset ) $str = substr_unicode ( $str , $offset , $length );
>
$pattern = $caseSensitive
? ‘~(?:’ . preg_quote ( $substr ) . ‘)~u’
: ‘~(?:’ . preg_quote ( $substr ) . ‘)~ui’ ;
preg_match_all ( $pattern , $str , $matches );
return isset( $matches [ 0 ]) ? count ( $matches [ 0 ]) : 0 ;
>
function substr_unicode ( $str , $start , $length = null ) return join ( » , array_slice (
preg_split ( ‘~~u’ , $str , — 1 , PREG_SPLIT_NO_EMPTY ), $start , $length ));
>
$s = ‘Ümit yüzüm gözüm. ‘ ;
print substr_count_unicode ( $s , ‘ü’ ); // 3
print substr_count_unicode ( $s , ‘ü’ , false ); // 4
print substr_count_unicode ( $s , ‘ü’ , false , 10 ); // 1
print substr_count_unicode ( $s , ‘üm’ ); // 2
print substr_count_unicode ( $s , ‘üm’ , false ); // 3
?>
This will handle a string where it is unknown if comma or period are used as thousand or decimal separator. Only exception where this leads to a conflict is when there is only a single comma or period and 3 possible decimals (123.456 or 123,456). An optional parameter is passed to handle this case (assume thousands, assume decimal, decimal when period, decimal when comma). It assumes an input string in any of the formats listed below.
function toFloat($pString, $seperatorOnConflict=»f»)
$decSeperator=».»;
$thSeperator=»»;
$pString=str_replace(» «, $thSeperator, $pString);
$firstPeriod=strpos($pString, «.»);
$firstComma=strpos($pString, «,»);
if($firstPeriod!==FALSE && $firstComma!==FALSE) if($firstPeriod <$firstComma) $pString=str_replace(".", $thSeperator, $pString);
$pString=str_replace(«,», $decSeperator, $pString);
>
else $pString=str_replace(«,», $thSeperator, $pString);
>
>
else if($firstPeriod!==FALSE || $firstComma!==FALSE) $seperator=$firstPeriod!==FALSE?».»:»,»;
if(substr_count($pString, $seperator)==1) $lastPeriodOrComma=strpos($pString, $seperator);
if($lastPeriodOrComma==(strlen($pString)-4) && ($seperatorOnConflict!=$seperator && $seperatorOnConflict!=»f»)) $pString=str_replace($seperator, $thSeperator, $pString);
>
else $pString=str_replace($seperator, $decSeperator, $pString);
>
>
else $pString=str_replace($seperator, $thSeperator, $pString);
>
>
return(float)$pString;
>