it-swarm-eu.dev

Jak zkrátit řetězec PHP na slovo, které je nejblíže určitému počtu znaků?

Mám fragment kódu napsaný v PHP, který vytáhne blok textu z databáze a odešle jej do widgetu na webové stránce. Původní blok textu může být dlouhý článek nebo krátká věta nebo dva; ale pro tento widget nemohu zobrazit více než, řekněme, 200 znaků. Mohl bych použít substr () pro sekání textu na 200 znaků, ale výsledek by byl odříznut uprostřed slov - to, co opravdu chci, je sekat text na konci posledního Slovo před 200 znaky.

170
Brian

Pomocí wordwrap funkce. Rozděluje texty do více řádků tak, že maximální šířka je ta, kterou jste zadali, a rozbíjí se na hranicích Wordu. Po rozdělení jednoduše vezmete první řádek:

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

Jedna věc, kterou tento oneliner neřeší, je případ, kdy samotný text je kratší než požadovaná šířka. Pro zvládnutí tohoto případu by měl člověk udělat něco jako:

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

Výše uvedené řešení má problém předčasného řezání textu, pokud obsahuje nový řádek před aktuálním bodem řezu. Zde je verze, která tento problém řeší:

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

Také zde je testovací třída PHPUnit používaná k testování implementace:

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

UPRAVIT :

Speciální znaky UTF8 jako 'à' nejsou zpracovány. Přidat 'u' na konci REGEXu pro zpracování:

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

212
Grey Panther

Tím se vrátí prvních 200 znaků slov:

preg_replace('/\s+?(\S+)?$/', '', substr($string, 0, 201));
125
mattmac
$WidgetText = substr($string, 0, strrpos(substr($string, 0, 200), ' '));

A tam to máte - spolehlivou metodu zkrácení jakéhokoliv řetězce na nejbližší celé slovo, zatímco zůstanete pod maximální délkou řetězce.

Vyzkoušela jsem další příklady a nevedla k žádoucímu výsledku.

42
Dave

Následující řešení se zrodilo, když jsem zaznamenal parametr $ break parametru wordwrap function:

řetězec wordwrap (řetězec $ str [ int $ width = 75 [ řetězec $ break = ")" n "[ bool $ cut = false]]]

Zde je řešení:

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

Příklad # 1.

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

Výše uvedený příklad zobrazí:

This is very long string...

Příklad # 2.

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

Výše uvedený příklad zobrazí:

This is short string.
33
Sergiy Sokolenko

Mějte na paměti, kdykoliv rozdělujete slovo „Word“ kdekoli, kde některé jazyky, například čínština a japonština, nepoužívají znak mezery k rozdělení slov. Uživatel se zlými úmysly by také mohl jednoduše zadat text bez mezer, nebo použít nějaký vzhled Unicode podobný standardnímu znaku prostoru. V takovém případě může jakékoli použité řešení skončit zobrazením celého textu. Cesta kolem toho může být kontrola délky řetězce po rozdělení na místa jako obvykle, pak, pokud je řetězec stále nad abnormálním limitem - možná 225 znaků v tomto případě - bude pokračovat a rozdělit ho na tuto hranici.

Ještě jedna věc s takovými věcmi, pokud jde o non-ASCII znaky; řetězce, které je obsahují, mohou být interpretovány standardním strlenem PHP () jako delší než ve skutečnosti jsou, protože jeden znak může mít místo jednoho jeden dva nebo více bajtů. Pokud použijete funkce strlen ()/substr () k rozdělení řetězců, můžete řetězec rozdělit uprostřed znaku! Pokud máte pochybnosti, mb_strlen () / mb_substr () jsou o něco více spolehlivé.

9
Garrett Albright

Použít strpos a 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;

To vám poskytne řetězec zkrácený na prvním místě po 30 znakech.

8
Lucas Oman

Zde je moje funkce založená na přístupu @ Cd-MaN.

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

  return $string;
}
5
Camsoft

Tady máš:

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
UnkwnTech

Je překvapující, jak složité je najít perfektní řešení tohoto problému. Na této stránce jsem ještě nenašel odpověď, která by neuspěla alespoň v některých situacích (zejména pokud řetězec obsahuje nové řádky nebo karty, nebo pokud je konec slova jiný než mezera, nebo pokud řetězec obsahuje řetězec UTF- 8 vícebajtových znaků).

Zde je jednoduché řešení, které funguje ve všech případech. Tam byly podobné odpovědi zde, ale "s" modifikátor je důležitý, pokud chcete, aby to práce s multi-line vstupu, a "u" modifikátor dělá to správně vyhodnotit UTF-8 multibyte znaků.

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

Jeden možný případ Edge s tímto ... pokud řetězec nemá žádné znaky v prvních znakech $ characterCount, vrátí celý řetězec. Pokud dáváte přednost přerušení na $ characterCount, i když to není hranice Wordu, můžete použít toto:

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

Jedna poslední volba, pokud chcete, aby byla přidána elipsa, pokud zkrátí řetězec ... 

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
orrd
$shorttext = preg_replace('/^([\s\S]{1,200})[\s]+?[\s\S]+/', '$1', $fulltext);

Popis:

  • ^ - začíná od začátku řetězce
  • ([\s\S]{1,200}) - dostanete od 1 do 200 libovolného znaku
  • [\s]+? - neobsahují mezery na konci krátkého textu, takže se můžeme vyhnout Word ... namísto Word...
  • [\s\S]+ - odpovídá všem ostatním obsahům

Testy:

  1. regex101.com let's přidat do or pár dalších r
  2. regex101.comorrrr přesně 200 znaků.
  3. regex101.com po pátém rorrrrr vyloučeno.

Užívat si.

3
hlcs

Ok, takže jsem dostal další verzi tohoto na základě výše uvedených odpovědí, ale brát více věcí v úvahu (utf-8, n a nbsp;), také řádek stripping wordpress shortcodes komentoval, pokud se používá s 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
Yo-L
/*
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);
}

Používání:

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

Tímto způsobem bude vydáno prvních 10 slov.

Funkce preg_split slouží k rozdělení řetězce do podřetězců. Hranice, podél kterých má být řetězec rozdělen, jsou zadány pomocí vzorce regulárních výrazů.

preg_split funkce trvá 4 parametry, ale pouze první 3 jsou pro nás aktuální.

První parametr - vzor První parametr je vzor regulárních výrazů, po kterém má být řetězec rozdělen. V našem případě chceme rozdělit řetězec přes hranice aplikace Word. Proto používáme předdefinovanou znakovou třídu \s, která odpovídá znakům v mezerách, jako je mezera, karta, návrat vozíku a posun řádku.

Druhý parametr - vstupní řetězec Druhý parametr je dlouhý textový řetězec, který chceme rozdělit.

Třetí parametr - limit Třetí parametr určuje počet dílčích řetězců, které mají být vráceny. Pokud nastavíte limit na n, preg_split vrátí pole n elementů. Prvky n-1 budou obsahovat podřetězce. Poslední prvek (n th) bude obsahovat zbytek řetězce.

2
bodi0

Použil bych k tomu funkci preg_match, protože to, co chcete, je docela jednoduchý výraz.

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

Výraz znamená "shodovat se s libovolným podřetězcem začínajícím od začátku délky 1-200, který končí mezerou." Výsledek je ve výsledku $ a zápas je v $ zápasy. To se stará o vaši původní otázku, která je specificky končící na jakémkoli prostoru. Pokud chcete, aby byl nový řádek ukončen, změňte regulární výraz na:

$result = preg_match("/^(.{1,199})[\n]/i", $text, $matches);
2
Justin Poliey

Takhle jsem to udělal:

$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
Shashank Saxena

Mám funkci, která dělá téměř to, co chcete, a pokud provedete několik úprav, vejde se přesně:

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

Na základě regexu @ 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]. '...';
}
1
amateur barista

Toto je malá oprava pro odpověď mattmac:

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

Jediný rozdíl je přidání mezery na konci řetězce $. To zajišťuje, že poslední slovo není odříznuto podle komentáře ReX357.

Nemám dost bodů, abych to přidal jako komentář.

1
tanc

Přidány příkazy IF/ELSEIF do kódu z Dave a AmalMurali pro zpracování řetězců bez mezer

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
jdorenbush

Vím, že je to staré, ale ...

function _truncate($str, $limit) {
    if(strlen($str) < $limit)
        return $str;
    $uid = uniqid();
    return array_shift(explode($uid, wordwrap($str, $limit, $uid)));
}
0
gosukiwi

Vytvářím více podobnou funkci jako substr a používám myšlenku @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 .: Plná délka řezu může být menší než substr.

0
evandro777

Věřím, že je to nejjednodušší způsob, jak to udělat:

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

Používám speciální znaky rozdělit text a snížit.

0
Namida

Použil jsem to dříve

<?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
Yousef Altaf

Zjistil jsem, že to funguje:

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

}

Vyrovnávací paměť umožňuje nastavit délku vráceného řetězce.

0
Mat Barnett

Použij toto: 

následující kód odstraní ','. Pokud máte jiný znak nebo dílčí řetězec, můžete jej použít místo výrazu „,“

substr($string, 0, strrpos(substr($string, 0, $comparingLength), ','))

// pokud máte jiný řetězec účtu 

substr($string, 0, strrpos(substr($string, 0, $comparingLength-strlen($currentString)), ','))
0
Mahbub Alam