У меня есть фрагмент кода, написанный на PHP, который извлекает блок текста из базы данных и отправляет его в виджет на веб-странице. Исходный блок текста может быть длинной статьей или коротким предложением или двумя; но для этого виджета я не могу отображать больше, скажем, 200 символов. Я мог бы использовать substr (), чтобы отрезать текст на 200 символов, но результат был бы обрезан посередине слов - что я действительно хочу, так это отрезать текст в конце последнего слово перед 200 символами.
Нет, не совсем. Вы можете установить шрифт надежным способом, а затем измерить худший сценарий, например, сколько самых широких символов поместится. И если вам нужно быть на 100% уверенным, как браузер его обработал, это больше не проблема PHP.
Попробуйте эту ссылку, может вам помочь stackoverflow.com/a/26098951/3944217
Вы можете найти s($str)->truncateSafely(200)
полезным, как указано в эта автономная библиотека.
Используя функцию перенос слова. Он разбивает текст на несколько строк таким образом, чтобы максимальная ширина была той, которую вы указали, с разрывом по границам слова. После разделения вы просто берете первую строку:
substr($string, 0, strpos(wordwrap($string, $your_desired_width), "\n"));
Единственная вещь, которую не обрабатывает этот oneliner, - это случай, когда сам текст короче желаемой ширины. Чтобы справиться с этим крайним случаем, нужно сделать что-то вроде:
if (strlen($string) > $your_desired_width)
{
$string = wordwrap($string, $your_desired_width);
$string = substr($string, 0, strpos($string, "\n"));
}
Вышеупомянутое решение имеет проблему преждевременного обрезания текста, если он содержит новую строку перед фактической точкой вырезания. Вот версия, которая решает эту проблему:
function tokenTruncate($string, $your_desired_width) {
$parts = preg_split('/([\s\n\r]+)/', $string, null, PREG_SPLIT_DELIM_CAPTURE);
$parts_count = count($parts);
$length = 0;
$last_part = 0;
for (; $last_part < $parts_count; ++$last_part) {
$length += strlen($parts[$last_part]);
if ($length > $your_desired_width) { break; }
}
return implode(array_slice($parts, 0, $last_part));
}
Кроме того, вот тестовый класс PHPUnit, используемый для тестирования реализации:
class TokenTruncateTest extends PHPUnit_Framework_TestCase {
public function testBasic() {
$this->assertEquals("1 3 5 7 9 ",
tokenTruncate("1 3 5 7 9 11 14", 10));
}
public function testEmptyString() {
$this->assertEquals("",
tokenTruncate("", 10));
}
public function testShortString() {
$this->assertEquals("1 3",
tokenTruncate("1 3", 10));
}
public function testStringTooLong() {
$this->assertEquals("",
tokenTruncate("toooooooooooolooooong", 10));
}
public function testContainingNewline() {
$this->assertEquals("1 3\n5 7 9 ",
tokenTruncate("1 3\n5 7 9 11 14", 10));
}
}
Специальные символы UTF8, такие как «à», не обрабатываются. Добавьте 'u' в конце REGEX, чтобы обработать его:
$parts = preg_split('/([\s\n\r]+)/u', $string, null, PREG_SPLIT_DELIM_CAPTURE);
Похоже, что текст будет преждевременно обрезан, если перед желаемой шириной будет \n
.
@KendallHopkins: правда, проблема действительно есть. Я обновил ответ альтернативной реализацией, которая решает данную проблему.
Будет ли этот пример работать для строки, содержащей html-теги, такие как теги абзаца?
это действительно помогло мне, моя головная боль была из-за длинных букв Arabic
и теперь она уменьшена до правильных слов с помощью функции tokenTruncate
.. спасибо миллион :)
Почему бы не добавить: if (strlen ($ string) <= $ your_desired_width) return $ string; как первое заявление?
Для однострочника tokenTruncate()
используйте символ ascii unit seperator
, для чего он предназначен: substr($content, 0, strpos(wordwrap($content, 350, chr(31)), chr(31)))
Код на самом деле неверен и вернет длину, превышающую желаемую.
Любить это! Я добавил обрезку () вокруг окончательного возвращаемого значения. В противном случае вы получите конечный пробел.
Будьте осторожны, используйте mb_substr
вместо substr
при использовании UTF-8.
Используйте strpos и substr:
<?php
$longString = "I have a code snippet written in PHP that pulls a block of text.";
$truncated = substr($longString,0,strpos($longString,' ',30));
echo $truncated;
Это даст вам строку, усеченную до первого пробела после 30 символов.
Привет, если длина строки без пробела будет меньше 30, будет возвращена ошибка. и здесь результат будет из первых 31 символа, а не из 30 ..
Ну вот:
function neat_trim($str, $n, $delim='…') {
$len = strlen($str);
if ($len > $n) {
preg_match('/(.{' . $n . '}.*?)\b/', $str, $matches);
return rtrim($matches[1]) . $delim;
}
else {
return $str;
}
}
Спасибо, я нашел вашу функцию самой полезной и надежной из всех этих ответов для моих нужд. Однако как я могу заставить его поддерживать многобайтовые строки?
Вроде поддерживает многобайтовую
Я бы использовал для этого функцию preg_match, так как вы хотите получить довольно простое выражение.
$matches = array();
$result = preg_match("/^(.{1,199})[\s]/i", $text, $matches);
Выражение означает «сопоставить любую подстроку, начинающуюся с начала длины 1-200 и заканчивающуюся пробелом». Результат - в $ result, а совпадение - в $ match. Это позаботится о вашем исходном вопросе, который конкретно заканчивается на любом месте. Если вы хотите, чтобы он заканчивался переводом строки, измените регулярное выражение на:
$result = preg_match("/^(.{1,199})[\n]/i", $text, $matches);
Это вернет первые 200 символов слов:
preg_replace('/\s+?(\S+)?$/', '', substr($string, 0, 201));
Почти. Кажется, что он убирает последнее слово предложения, несмотря ни на что.
отлично работает, но я обнаружил ту же ошибку, что и ReX357. Когда есть более 1 слова, оно удаляет последнее.
Просто заверните его в чек, чтобы убедиться, что строка длиннее, чем вы тестируете (как принятый ответ) if (strlen($string) > $your_desired_width) { preg_replace(...); }
Я отредактировал ответ, включив совет @BlairMcMillan
ВСЕГДА УДАЛЯЕТ ПОСЛЕДНЕЕ СЛОВО: P
Небольшое улучшение регулярного выражения: круглые скобки делают последний \ S + необязательным для соответствия, но они также захватывают эти символы. Поскольку нам не нужно захватывать эти символы, сделайте круглые скобки не захватывающими, например: /\s+?(?:\S+)?$/
@fubar не вижу, как это помогает. Разве результат substr () не должен быть таким же, учитывая, что строка обрезается до определенной длины?
@JohnLaw, да, я понятия не имею, о чем я думал, когда опубликовал этот комментарий.
Имейте в виду, что когда вы разбиваете слова по слову, некоторые языки, такие как китайский и японский, не используют пробел для разделения слов. Кроме того, злоумышленник может просто ввести текст без пробелов или использовать какой-либо Unicode, похожий на стандартный символ пробела, и в этом случае любое используемое вами решение может в конечном итоге отобразить весь текст в любом случае. Способом обойти это может быть проверка длины строки после разделения ее на пробелы как обычно, а затем, если строка все еще превышает ненормальный предел - может быть, в этом случае 225 символов - продолжая и тупо разбивая ее на этом пределе.
Еще одно предостережение относительно таких вещей, когда речь идет о символах, отличных от ASCII; строки, содержащие их, могут интерпретироваться стандартной функцией PHP strlen () как более длинные, чем они есть на самом деле, потому что один символ может занимать два или более байта вместо одного. Если вы просто используете функции strlen () / substr () для разделения строк, вы можете разделить строку посередине символа! Если есть сомнения, mb_strlen () / mb_substr () немного более надежны.
Вот моя функция, основанная на подходе @Cd-MaN.
function shorten($string, $width) {
if (strlen($string) > $width) {
$string = wordwrap($string, $width);
$string = substr($string, 0, strpos($string, "\n"));
}
return $string;
}
На основе регулярного выражения @Justin Poliey:
// Trim very long text to 120 characters. Add an ellipsis if the text is trimmed.
if (strlen($very_long_text) > 120) {
$matches = array();
preg_match("/^(.{1,120})[\s]/i", $very_long_text, $matches);
$trimmed_text = $matches[0]. '...';
}
$WidgetText = substr($string, 0, strrpos(substr($string, 0, 200), ' '));
И вот он - надежный метод усечения любой строки до ближайшего целого слова, при этом оставаясь ниже максимальной длины строки.
Я пробовал другие примеры, приведенные выше, и они не дали желаемых результатов.
Если заданная длина строки меньше максимальной длины, это отрежет все до последнего пробела. Чтобы этого избежать, заключите это в оператор if
: if (strlen($str) > 200) { ... }
Просто и, вероятно, намного быстрее, чем другие решения.
Одна из проблем заключается в том, что он возвращает пустую строку, если строка не содержит пробела.
Может быть упрощено до: $WidgetText = substr($string, 0, strpos($string, ' ', 200));
Хорошо, поэтому я получил другую версию этого, основанную на приведенных выше ответах, но с учетом большего количества вещей (utf-8, \ n и & nbsp;), а также строку, удаляющую короткие коды wordpress, прокомментированные, если они используются с wp.
function neatest_trim($content, $chars)
if (strlen($content) > $chars)
{
$content = str_replace(' ', ' ', $content);
$content = str_replace("\n", '', $content);
// use with wordpress
//$content = strip_tags(strip_shortcodes(trim($content)));
$content = strip_tags(trim($content));
$content = preg_replace('/\s+?(\S+)?$/', '', mb_substr($content, 0, $chars));
$content = trim($content) . '...';
return $content;
}
Это небольшое исправление для ответа mattmac:
preg_replace('/\s+?(\S+)?$/', '', substr($string . ' ', 0, 201));
Единственное отличие состоит в том, чтобы добавить пробел в конец строки $. Это гарантирует, что последнее слово не обрезается в соответствии с комментарием ReX357.
У меня недостаточно очков репутации, чтобы добавить это в качестве комментария.
/*
Cut the string without breaking any words, UTF-8 aware
* param string $str The text string to split
* param integer $start The start position, defaults to 0
* param integer $words The number of words to extract, defaults to 15
*/
function wordCutString($str, $start = 0, $words = 15 ) {
$arr = preg_split("/[\s]+/", $str, $words+1);
$arr = array_slice($arr, $start, $words);
return join(' ', $arr);
}
Использование:
$input = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna liqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.';
echo wordCutString($input, 0, 10);
Это выведет первые 10 слов.
Функция preg_split
используется для разделения строки на подстроки. Границы, по которым строка должна быть разделена, указываются с использованием шаблона регулярных выражений.
Функция preg_split
принимает 4 параметра, но сейчас для нас актуальны только первые 3.
Первый параметр - паттерн
Первый параметр - это шаблон регулярных выражений, по которому должна быть разбита строка. В нашем случае мы хотим разбить строку по границам слова. Поэтому мы используем предопределенный класс символов \s
, который соответствует символам пробела, таким как пробел, табуляция, возврат каретки и перевод строки.
Второй параметр - входная строка Второй параметр - длинная текстовая строка, которую мы хотим разбить.
Третий параметр - предел
Третий параметр указывает количество возвращаемых подстрок. Если вы установите ограничение на n
, preg_split вернет массив из n элементов. Первые элементы n-1
будут содержать подстроки. Последний элемент (n th)
будет содержать оставшуюся часть строки.
Я знаю, что это старое, но ...
function _truncate($str, $limit) {
if (strlen($str) < $limit)
return $str;
$uid = uniqid();
return array_shift(explode($uid, wordwrap($str, $limit, $uid)));
}
Следующее решение родилось, когда я заметил параметр $ break функции перенос слова:
string wordwrap ( string $str [, int $width = 75 [, string $break = "\n" [, bool $cut = false ]]] )
Вот решение:
/**
* Truncates the given string at the specified length.
*
* @param string $str The input string.
* @param int $width The number of chars at which the string will be truncated.
* @return string
*/
function truncate($str, $width) {
return strtok(wordwrap($str, $width, "...\n"), "\n");
}
Пример №1.
print truncate("This is very long string with many chars.", 25);
Приведенный выше пример выведет:
This is very long string...
Пример №2.
print truncate("This is short string.", 25);
Приведенный выше пример выведет:
This is short string.
это не работает, если в строке уже есть новый строковый символ (например, если вы пытаетесь извлечь description
из сообщения в блоге)
@supersan Всегда может выполнить предварительную обработку с помощью preg_replace('/\s+/', ' ', $description)
, чтобы заменить все пробельные символы одним пробелом;)
Я использовал это раньше
<?php
$your_desired_width = 200;
$string = $var->content;
if (strlen($string) > $your_desired_width) {
$string = wordwrap($string, $your_desired_width);
$string = substr($string, 0, strpos($string, "\n")) . " More...";
}
echo $string;
?>
Может быть, это кому-то поможет:
<?php
$string = "Your line of text";
$spl = preg_match("/([, \.\d\-''\"\"_()]*\w+[, \.\d\-''\"\"_()]*){50}/", $string, $matches);
if (isset($matches[0])) {
$matches[0] .= "...";
echo "<br />" . $matches[0];
} else {
echo "<br />" . $string;
}
?>
У меня есть функция, которая делает почти то, что вы хотите, если вы сделаете несколько правок, она точно подойдет:
<?php
function stripByWords($string,$length,$delimiter = '<br>') {
$words_array = explode(" ",$string);
$strlen = 0;
$return = '';
foreach($words_array as $word) {
$strlen += mb_strlen($word,'utf8');
$return .= $word." ";
if ($strlen >= $length) {
$strlen = 0;
$return .= $delimiter;
}
}
return $return;
}
?>
$shorttext = preg_replace('/^([\s\S]{1,200})[\s]+?[\s\S]+/', '', $fulltext);
Описание:
^
- начать с начала строки([\s\S]{1,200})
- получить от 1 до 200 любого персонажа[\s]+?
- не включать пробелы в конце короткого текста, поэтому мы можем избежать word ...
вместо word...
[\s\S]+
- соответствует всему остальному контентуТесты:
regex101.com
добавим к or
еще несколько r
regex101.com
orrrr
ровно 200 символов.regex101.com
после пятого r
orrrrr
исключен.Наслаждаться.
я не понимаю документацию PHP. Я знаю, что - это «замена», но в этом конкретном контексте, о чем он? пустая переменная?
@Anthony ссылка для соответствия внутри скобок
([\s\S]{1,200})
. будет ссылаться на две вторые пары скобок, если они есть в шаблоне.
Вот как я это сделал:
$string = "I appreciate your service & idea to provide the branded toys at a fair rent price. This is really a wonderful to watch the kid not just playing with variety of toys but learning faster compare to the other kids who are not using the BooksandBeyond service. We wish you all the best";
print_r(substr($string, 0, strpos(wordwrap($string, 250), "\n")));
Я создаю функцию, более похожую на substr, и использую идею @Dave.
function substr_full_word($str, $start, $end){
$pos_ini = ($start == 0) ? $start : stripos(substr($str, $start, $end), ' ') + $start;
if (strlen($str) > $end){ $pos_end = strrpos(substr($str, 0, ($end + 1)), ' '); } // IF STRING SIZE IS LESSER THAN END
if (empty($pos_end)){ $pos_end = $end; } // FALLBACK
return substr($str, $pos_ini, $pos_end);
}
Ps .: Длина отреза может быть меньше подстр.
Здесь вы можете попробовать это
substr( $str, 0, strpos($str, ' ', 200) );
Это решение уже упоминалось в других ответах. Проблема в том, что он не работает, если длина строки меньше 200 символов или если она не содержит пробелов. Он также не ограничивает строку 200 символами, вместо этого он разбивает строку на пробел после 200 символов, что обычно не то, что вам нужно.
Удивительно, насколько сложно найти идеальное решение этой проблемы. Я еще не нашел на этой странице ответа, который не дает сбоев, по крайней мере, в некоторых ситуациях (особенно, если строка содержит символы новой строки или табуляции, или если разрыв слова является чем-то другим, кроме пробела, или если строка имеет UTF- 8 многобайтовых символов).
Вот простое решение, которое работает во всех случаях. Здесь были похожие ответы, но модификатор «s» важен, если вы хотите, чтобы он работал с многострочным вводом, а модификатор «u» позволяет правильно оценивать многобайтовые символы UTF-8.
function wholeWordTruncate($s, $characterCount)
{
if (preg_match("/^.{1,$characterCount}\b/su", $s, $match)) return $match[0];
return $s;
}
Один из возможных крайних случаев с этим ... если строка вообще не имеет пробелов в первых символах $ characterCount, она вернет всю строку. Если вы предпочитаете, чтобы он заставлял разрыв в $ characterCount, даже если это не граница слова, вы можете использовать это:
function wholeWordTruncate($s, $characterCount)
{
if (preg_match("/^.{1,$characterCount}\b/su", $s, $match)) return $match[0];
return mb_substr($return, 0, $characterCount);
}
Последний вариант, если вы хотите, чтобы он добавлял многоточие, если он усекает строку ...
function wholeWordTruncate($s, $characterCount, $addEllipsis = ' …')
{
$return = $s;
if (preg_match("/^.{1,$characterCount}\b/su", $s, $match))
$return = $match[0];
else
$return = mb_substr($return, 0, $characterCount);
if (strlen($s) > strlen($return)) $return .= $addEllipsis;
return $return;
}
Я полностью согласен с вами, ваш, кажется, один из редких примеров, который работает повсеместно со всеми языками Unicode. Отличная работа!
Я привел пример, основанный на вашем ответе здесь: github.com/thlib/php-truncate-words Это стало одним лайнером, вы могли бы быстро найти ошибки?
Добавлены операторы IF / ELSEIF в код из Дэйв и АмальМурали для обработки строк без пробелов
if ((strpos($string, ' ') !== false) && (strlen($string) > 200)) {
$WidgetText = substr($string, 0, strrpos(substr($string, 0, 200), ' '));
}
elseif (strlen($string) > 200) {
$WidgetText = substr($string, 0, 200);
}
Я считаю, что это самый простой способ сделать это:
$lines = explode('♦♣♠',wordwrap($string, $length, '♦♣♠'));
$newstring = $lines[0] . ' • • •';
Я использую специальные символы, чтобы разделить текст и вырезать его.
Я считаю, что это работает:
функция abbreviate_string_to_whole_word ($ строка, $ max_length, $ buffer) {
if (strlen($string)>$max_length) {
$string_cropped=substr($string,0,$max_length-$buffer);
$last_space=strrpos($string_cropped, " ");
if ($last_space>0) {
$string_cropped=substr($string_cropped,0,$last_space);
}
$abbreviated_string=$string_cropped." ...";
}
else {
$abbreviated_string=$string;
}
return $abbreviated_string;
}
Буфер позволяет вам регулировать длину возвращаемой строки.
Использовать это:
следующий код удалит ','. Если у вас есть другой символ или подстрока, вы можете использовать его вместо ','
substr($string, 0, strrpos(substr($string, 0, $comparingLength), ','))
// если у вас есть другая строковая учетная запись для
substr($string, 0, strrpos(substr($string, 0, $comparingLength-strlen($currentString)), ','))
Хотя это довольно старый вопрос, я решил, что предоставлю альтернативу, поскольку он не упоминался и действителен для PHP 4.3+.
Вы можете использовать семейство функций sprintf
для усечения текста с помощью модификатора точности %.ℕs
.
A period
.
followed by an integer who's meaning depends on the specifier:
- For e, E, f and F specifiers: this is the number of digits to be printed after the decimal point (by default, this is 6).
- For g and G specifiers: this is the maximum number of significant digits to be printed.
- For s specifier: it acts as a cutoff point, setting a maximum character limit to the string
$string = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var_dump(sprintf('%.10s', $string));
Результат
string(10) "0123456789"
Поскольку sprintf
работает аналогично substr
и частично обрезает слова. Приведенный ниже подход гарантирует, что слова не будут обрезаны с помощью strpos(wordwrap(..., '[break]'), '[break]')
со специальным разделителем. Это позволяет нам получить позицию и убедиться, что мы не совпадаем со стандартными структурами предложений.
Возврат строки без частичного обрезания слов и не превышающей заданную ширину, при желании с сохранением разрывов строк.
function truncate($string, $width, $on = '[break]') {
if (strlen($string) > $width && false !== ($p = strpos(wordwrap($string, $width, $on), $on))) {
$string = sprintf('%.'. $p . 's', $string);
}
return $string;
}
var_dump(truncate('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ', 20));
var_dump(truncate("Lorem Ipsum is simply dummy text of the printing and typesetting industry.", 20));
var_dump(truncate("Lorem Ipsum\nis simply dummy text of the printing and typesetting industry.", 20));
Результат
/*
string(36) "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
string(14) "Lorem Ipsum is"
string(14) "Lorem Ipsum
is"
*/
Результаты с использованием wordwrap($string, $width)
или strtok(wordwrap($string, $width), "\n")
/*
string(14) "Lorem Ipsum is"
string(11) "Lorem Ipsum"
*/
// a looonnng string ...
$str = "Le Lorem Ipsum est simplement du
faux texte employé dans la composition et
la mise en page avant impression.
Le Lorem Ipsum est le faux texte standard de
l'imprimerie depuis les années 1500, quand un
imprimeur anonyme assembla ensemble des morceaux
de texte pour réaliser un livre spécimen de polices
de texte. Il n'a pas fait que survivre cinq siècles,
mais s'est aussi adapté à la bureautique informatique,
sans que son contenu n'en soit modifié. Il a été
popularisé dans les années 1960 grâce à la vente
de feuilles Letraset contenant des passages du
Lorem Ipsum, et, plus récemment, par son inclusion
dans des applications de mise en page de texte,
comme Aldus PageMaker";
// number chars to cut
$number_to_cut = 300;
// string truncated in one line !
$truncated_string =
substr($str, 0, strrpos(substr($str, 0, $number_to_cut), ' '));
// test return
echo $truncated_string;
// variation (add ellipsis) : echo $truncated_string.' ...';
// output :
/* Le Lorem Ipsum est simplement du
faux texte employé dans la composition et
la mise en page avant impression.
Le Lorem Ipsum est le faux texte standard de
l'imprimerie depuis les années 1500, quand un
imprimeur anonyme assembla ensemble des morceaux
de texte pour réaliser un livre
*/
Вы можете использовать это:
function word_shortener($text, $words=10, $sp='...'){
$all = explode(' ', $text);
$str = '';
$count = 1;
foreach($all as $key){
$str .= $key . ($count >= $words ? '' : ' ');
$count++;
if ($count > $words){
break;
}
}
return $str . (count($all) <= $words ? '' : $sp);
}
Примеры:
word_shortener("Hello world, this is a text", 3); // Hello world, this...
word_shortener("Hello world, this is a text", 3, ''); // Hello world, this
word_shortener("Hello world, this is a text", 3, '[read more]'); // Hello world, this[read more]
Вопрос предназначен для того, чтобы сказать, что усеченный текст уместится в некотором фиксированном количестве пикселей на веб-странице. В этом случае, в зависимости от выбранного шрифта, пространство, необходимое для каждого символа, не является постоянным. И поэтому мы не можем предположить, что 200 символов лучше всего уместятся в доступных пикселях. Пока (до 2 марта 2011 г.) во всех приведенных ниже ответах отсутствует этот момент, и, следовательно, ни один из них не обеспечивает надежного решения. - :(