Jak obciąć łańcuch znaków w PHP do słowa najbliższego określonej liczbie znaków?

Mam fragment kodu napisany w PHP, który pobiera blok tekstu z bazy danych i wysyła go do widżetu na stronie internetowej. Oryginalny blok tekstu może być długi artykuł lub krótkie zdanie lub dwa; ale dla tego widżetu nie mogę wyświetlić więcej niż, powiedzmy, 200 znaków. Mógłbym użyć substr (), aby odciąć tekst na 200 znakach, ale rezultatem byłoby odcięcie w środku słów-to, czego naprawdę chcę, to odcięcie tekstu na końcu ostatniego Słowa przed 200 chars.

Author: Brian, 2008-09-17

25 answers

Za pomocą funkcji wordwrap . Dzieli teksty na wiele linii tak, że maksymalna szerokość jest taka, jaką podałeś, przełamując granice słów. Po rozdzieleniu, po prostu wykonujesz pierwszą linię:

substr($string, 0, strpos(wordwrap($string, $your_desired_width), "\n"));

Jedną z rzeczy, której ten jeden nie obsługuje, jest przypadek, gdy sam tekst jest krótszy niż pożądana szerokość. Aby poradzić sobie z tym edge-case, należy zrobić coś takiego:

if (strlen($string) > $your_desired_width) 
{
    $string = wordwrap($string, $your_desired_width);
    $string = substr($string, 0, strpos($string, "\n"));
}

Powyższe rozwiązanie ma problem przedwczesnego cięcia tekstu, jeśli zawiera znak nowej linii przed faktycznym punktem przecięcia. Oto wersja, która rozwiązuje ten problem:

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));
}

Również, tutaj jest PHPUnit testclass używany do testowania implementacji:

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));
  }
}

EDIT:

Specjalne znaki UTF8, takie jak 'à', nie są obsługiwane. Dodaj' u ' na końcu wyrażenia regularnego, aby go obsłużyć:

$parts = preg_split('/([\s\n\r]+)/u', $string, null, PREG_SPLIT_DELIM_CAPTURE);

 210
Author: Grey Panther,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-09 20:32:10

To zwróci pierwsze 200 znaków słów:

preg_replace('/\s+?(\S+)?$/', '', substr($string, 0, 201));
 121
Author: mattmac,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-17 04:41:34
$WidgetText = substr($string, 0, strrpos(substr($string, 0, 200), ' '));

I oto jest-niezawodna metoda obcinania dowolnego ciągu do najbliższego całego słowa, przy zachowaniu maksymalnej długości łańcucha.

Próbowałem innych przykładów powyżej i nie przyniosły pożądanych rezultatów.

 42
Author: Dave,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-12 04:29:50

Poniższe rozwiązanie powstało, gdy zauważyłem parametr $ break wordwrap funkcji:

String Wordwrap ( string $str [, int $width = 75 [, string $break = "\n " [, bool $cut = false]]])

Oto rozwiązanie :

/**
 * 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");
}

Przykład #1.

print truncate("This is very long string with many chars.", 25);

Powyższy przykład wyświetli:

This is very long string...

Przykład # 2.

print truncate("This is short string.", 25);

Powyższy przykład wyświetli:

This is short string.
 32
Author: Sergiy Sokolenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-20 13:19:35

Zawsze, gdy dzielisz słowo, pamiętaj, że niektóre języki, takie jak Chiński i japoński, nie używają znaku spacji do dzielenia słów. Ponadto złośliwy użytkownik może po prostu wprowadzić tekst bez spacji lub użyć jakiegoś Unicode podobnego do standardowego znaku spacji, w którym to przypadku dowolne rozwiązanie może wyświetlać cały tekst i tak. Sposobem na obejście tego może być sprawdzenie długości łańcucha po podzieleniu go na spacje jako normalne, wtedy, jeśli łańcuch jest nadal powyżej nienormalnego limitu-może 225 znaków w tym przypadku-idąc dalej i dzieląc go głupio na tym limicie.

Jeszcze jedno zastrzeżenie z takimi rzeczami, jeśli chodzi o znaki inne niż ASCII; łańcuchy zawierające je mogą być interpretowane przez standard PHP strlen () jako dłuższe niż w rzeczywistości, ponieważ pojedynczy znak może zająć dwa lub więcej bajtów zamiast tylko jednego. Jeśli po prostu użyjesz funkcji strlen()/substr() do podziału łańcuchów, możesz podzielić łańcuch w środku charakter! Gdy masz wątpliwości, mb_strlen()/mb_substr () są trochę bardziej niezawodne.

 9
Author: Garrett Albright,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-06 18:18:36

Użyj strpos i podstr:

<?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;

To da ci łańcuch obcięty w pierwszej spacji po 30 znakach.

 8
Author: Lucas Oman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-03 19:28:54

Oto moja funkcja oparta na podejściu @ Cd-MaN.

function shorten($string, $width) {
  if(strlen($string) > $width) {
    $string = wordwrap($string, $width);
    $string = substr($string, 0, strpos($string, "\n"));
  }

  return $string;
}
 5
Author: Camsoft,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-26 12:36:03

Proszę bardzo:

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;
   }
}
 4
Author: UnkwnTech,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-17 04:31:26

To zaskakujące, jak trudne jest znalezienie idealnego rozwiązania tego problemu. Nie znalazłem jeszcze odpowiedzi na tej stronie, która nie zawiedzie przynajmniej w niektórych sytuacjach(zwłaszcza jeśli łańcuch zawiera nowe linie lub tabulatory, lub jeśli słowo break jest czymś innym niż spacja, lub jeśli łańcuch ma wielobajtowe znaki UTF-8).

Oto proste rozwiązanie, które działa we wszystkich przypadkach. Były tu podobne odpowiedzi, ale modyfikator "s" jest ważny, jeśli chcesz, aby działał z wejście Wielowierszowe, a modyfikator" u " umożliwia poprawną ocenę wielobajtowych znaków UTF-8.

function wholeWordTruncate($s, $characterCount) 
{
    if (preg_match("/^.{1,$characterCount}\b/su", $s, $match)) return $match[0];
    return $s;
}

Jeden możliwy przypadek krawędzi z tym... jeśli łańcuch znaków nie ma żadnych białych znaków w pierwszych znakach $characterCount, zwróci cały łańcuch. Jeśli wolisz wymusza łamanie przy $characterCount, nawet jeśli nie jest to granica słowa, Możesz użyć tego:

function wholeWordTruncate($s, $characterCount) 
{
    if (preg_match("/^.{1,$characterCount}\b/su", $s, $match)) return $match[0];
    return mb_substr($return, 0, $characterCount);
}

Ostatnia opcja, jeśli chcesz dodać wielokropek, jeśli obcina łańcuch...

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;
}
 3
Author: orrd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-01 21:04:36
$shorttext = preg_replace('/^([\s\S]{1,200})[\s]+?[\s\S]+/', '$1', $fulltext);

Opis:

  • ^ - Początek od początku łańcucha
  • ([\s\S]{1,200}) - Uzyskaj od 1 do 200 dowolnego znaku
  • [\s]+? - nie zawierają spacji na końcu krótkiego tekstu, więc możemy uniknąć word ... zamiast word...
  • [\s\S]+ - dopasuj wszystkie inne treści

Testy:

  1. regex101.com dodajmy do or kilka innych r
  2. regex101.com orrrr dokładnie 200 znaków.
  3. regex101.com Po piąty r orrrrr wykluczone.
Smacznego.
 3
Author: hlcs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-04 17:06:08

Użyłbym do tego funkcji preg_match, ponieważ to, co chcesz, jest dość prostym wyrażeniem.

$matches = array();
$result = preg_match("/^(.{1,199})[\s]/i", $text, $matches);

Wyrażenie oznacza " dopasuj dowolny podłańcuch zaczynający się od początku długości 1-200, który kończy się spacją."Wynik jest w $ result, a mecz jest w $ matches. To zajmie się Twoim oryginalnym pytaniem, które konkretnie kończy się na każdej przestrzeni. Jeśli chcesz, aby kończyło się na nowych liniach, Zmień Wyrażenie regularne na:

$result = preg_match("/^(.{1,199})[\n]/i", $text, $matches);
 2
Author: Justin Poliey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-17 04:59:08

Ok więc mam inną wersję tego na podstawie powyższych odpowiedzi, ale biorąc pod uwagę więcej rzeczy(utf-8, \n i  ), również linia stripping WordPress shortcodes skomentowane jeśli używane z wp.

function neatest_trim($content, $chars) 
  if (strlen($content) > $chars) 
  {
    $content = str_replace('&nbsp;', ' ', $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;
  }
 2
Author: Yo-L,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-28 10:52:18
/*
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);
}

Użycie:

$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); 

To wyświetli pierwsze 10 słów.

Funkcja preg_split służy do dzielenia łańcucha na podłańcuchy. Granice, wzdłuż których łańcuch ma być podzielony, są określone za pomocą wzorca wyrażeń regularnych.

preg_split funkcja pobiera 4 parametry, ale tylko pierwsze 3 są dla nas istotne w tej chwili.

Pierwszy Parametr-Wzór Pierwszym parametrem jest wzorzec wyrażeń regularnych, wzdłuż którego łańcuch ma być podzielony. W naszym przypadku chcemy aby podzielić ciąg ponad granicami słowa. Dlatego używamy predefiniowanej klasy znaków \s, która pasuje do białych znaków, takich jak spacja, tabulator, powrót karetki i kanał linii.

Drugi Parametr-Łańcuch Wejściowy Drugim parametrem jest długi ciąg tekstowy, który chcemy podzielić.

Trzeci Parametr – Limit Trzeci parametr określa ilość podłańcuchów, które mają zostać zwrócone. Jeśli ustawisz limit na n, preg_split zwróci tablicę N elementów. Na pierwsze n-1 elementy będą zawierać podłańcuchy. Ostatni (n th) element będzie zawierał resztę ciągu znaków.

 2
Author: bodi0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-05 09:32:56

Na podstawie regex @ Justin Poley:

// 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]. '...';
}
 1
Author: amateur barista,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-09 16:28:08

To jest mała poprawka dla odpowiedzi mattmaca:

preg_replace('/\s+?(\S+)?$/', '', substr($string . ' ', 0, 201));

Jedyną różnicą jest dodanie spacji na końcu $string. Zapewnia to, że ostatnie słowo nie zostanie odcięte zgodnie z komentarzem ReX357.

Nie mam wystarczającej ilości punktów reputacji, aby dodać to jako komentarz.

 1
Author: tanc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-09 22:29:11

Mam funkcję, która robi prawie to, co chcesz, jeśli zrobisz kilka edycji, będzie pasować dokładnie:

<?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;
}
?>
 1
Author: Rikudou_Sennin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-13 11:37:05

Tak to zrobiłem:

$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")));
 1
Author: Shashank Saxena,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-18 12:20:43

Wiem, że to stare, ale.....

function _truncate($str, $limit) {
    if(strlen($str) < $limit)
        return $str;
    $uid = uniqid();
    return array_shift(explode($uid, wordwrap($str, $limit, $uid)));
}
 0
Author: gosukiwi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-26 12:53:52

Używałem tego wcześniej

<?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;
?>
 0
Author: Yousef Altaf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-08 11:31:18

Tworzę funkcję bardziej podobną do substr, używając idei @ 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.: Całkowita długość cięcia może być mniejsza niż podstr.

 0
Author: evandro777,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-25 14:27:18

Dodano polecenia IF/ELSEIF do kodu z Dave i AmalMurali do obsługi łańcuchów bez spacji

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);
}
 0
Author: jdorenbush,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:37

Uważam, że jest to najprostszy sposób, aby to zrobić:

$lines = explode('♦♣♠',wordwrap($string, $length, '♦♣♠'));
$newstring = $lines[0] . ' &bull; &bull; &bull;';

Używam znaków specjalnych, aby podzielić tekst i wyciąć go.

 0
Author: Namida,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-09 13:26:34

Uważam, że to działa:

Function abbreviate_string_to_whole_word ($string,$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."&nbsp;...";
}
else {
    $abbreviated_string=$string;
}

return $abbreviated_string;

}

Bufor pozwala dostosować długość zwracanego ciągu.

 0
Author: Mat Barnett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-11 11:00:25

Tutaj możesz spróbować tego

substr( $str, 0, strpos($str, ' ', 200) ); 
 -1
Author: Abhijeet kumar sharma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-26 12:46:05

Może to komuś pomoże:

<?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;
    }

?>
 -2
Author: slash3b,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-01 14:31:24