PHPで文字列を特定の文字数に最も近い単語に切り詰める方法は?

StackOverflow https://stackoverflow.com/questions/79960

  •  09-06-2019
  •  | 
  •  

質問

データベースからテキストのブロックを取得し、それを Web ページ上のウィジェットに送信する PHP で書かれたコード スニペットがあります。元のテキスト ブロックは、長い記事でも、1 つまたは 2 つの短い文でも構いません。しかし、このウィジェットでは、たとえば 200 文字を超える文字を表示することはできません。substr() を使用してテキストを 200 文字で切り取ることもできますが、結果は単語の途中で切り取られることになります。私が本当に望んでいるのは、最後のテキストの終わりでテキストを切り取ることです。 言葉 200文字まで。

役に立ちましたか?

解決

を使用することで、 ワードラップ 関数。指定した最大幅になるようにテキストを複数行に分割し、単語の境界で分割します。分割後は、最初の行を取得するだけです。

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

このワンライナーが処理できないことの 1 つは、テキスト自体が希望の幅より短い場合です。この特殊なケースに対処するには、次のようなことを行う必要があります。

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 文字は処理されません。これを処理するには、REGEX の末尾に「u」を追加します。

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

他のヒント

これにより、単語の最初の 200 文字が返されます。

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

これは、最大文字列長を超えないようにして、任意の文字列を最も近い単語全体に切り詰める信頼性の高い方法です。

上記の他の例も試してみましたが、望ましい結果は得られませんでした。

次のソリューションは、 $break パラメータに気づいたときに生まれました。 ワードラップ 関数:

string wordwerswrap(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.

「単語」で分割する場合は常に、中国語や日本語などの一部の言語では単語の分割にスペース文字を使用しないことに留意してください。また、悪意のあるユーザーは、単純にスペースなしでテキストを入力したり、標準のスペース文字に似た Unicode 文字を使用したりする可能性があります。その場合、どのソリューションを使用してもテキスト全体が表示される可能性があります。これを回避する方法は、通常どおりスペースで文字列を分割した後、文字列の長さをチェックし、文字列がまだ異常な制限 (この場合はおそらく 225 文字) を超えている場合は、その制限で愚かに分割することです。

このような非 ASCII 文字に関しては、もう 1 つ注意点があります。これらを含む文字列は、PHP の標準 strlen() によって実際よりも長く解釈される場合があります。これは、1 つの文字が 1 バイトではなく 2 バイト以上かかる場合があるためです。strlen()/substr() 関数を使用して文字列を分割するだけの場合、文字の途中で文字列を分割する可能性があります。迷ったときは、 mb_strlen()/mb_substr() もう少し確実です。

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 文字後の最初のスペースで切り詰められた文字列が得られます。

これは@Cd-MaNのアプローチに基づいた私の関数です。

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

  return $string;
}

どうぞ:

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

この問題に対する完璧な解決策を見つけるのがどれほど難しいかは驚くべきことです。このページでは、少なくともいくつかの状況(特に文字列に改行やタブが含まれている場合、単語の区切りがスペース以外である場合、または文字列に UTF- が含まれている場合)で失敗しない答えをまだ見つけていません。 8 つのマルチバイト文字)。

ここでは、あらゆる場合に機能する簡単な解決策を紹介します。ここにも同様の回答がありましたが、複数行入力で動作させるには「s」修飾子が重要であり、「u」修飾子を使用すると UTF-8 マルチバイト文字を正しく評価できるようになります。

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

これに関して考えられるエッジケースの 1 つは...文字列の最初の $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;
}
$shorttext = preg_replace('/^([\s\S]{1,200})[\s]+?[\s\S]+/', '$1', $fulltext);

説明:

  • ^ - 文字列の先頭から開始
  • ([\s\S]{1,200}) - 任意の文字の 1 ~ 200 を取得します
  • [\s]+? - 短いテキストの末尾にスペースを含めないでください。 word ... の代わりに word...
  • [\s\S]+ - 他のすべてのコンテンツと一致する

テスト:

  1. regex101.com に追加しましょう or 他にいくつか r
  2. regex101.com orrrr ちょうど200文字。
  3. regex101.com 5番目以降 r orrrrr 除外されます。

楽しむ。

必要なのは非常に単純な式なので、これを行うには preg_match 関数を使用します。

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

この式は、「スペースで終わる長さ1〜200の開始から始まるサブストリングと一致する」を意味します。結果は$ resultで、試合は$マッチになります。これで、特に任意のスペースで終わるという元の質問が解決されます。改行で終わるようにしたい場合は、正規表現を次のように変更します。

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

さて、私は上記の回答に基づいてこれの別のバージョンを入手しましたが、より多くのことを考慮しています(utf-8、 、および )、wp で使用した場合にコメントされた wordpress ショートコードを削除する行も含まれます。

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;
  }
/*
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 スペース、タブ、キャリッジリターン、ラインフィードなどの空白文字と一致します。

2番目のパラメーター - 入力文字列2番目のパラメーターは、分割する長いテキスト文字列です。

3番目のパラメーター - 制限3番目のパラメーターは、返される必要のあるサブストリングの数を指定します。制限を次のように設定すると、 n, preg_split は n 個の要素の配列を返します。最初 n-1 要素には部分文字列が含まれます。最後 (n th) 要素には文字列の残りの部分が含まれます。

@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]. '...';
}

これは mattmac の答えに対する小さな修正です。

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

唯一の違いは、$string の末尾にスペースを追加することです。これにより、ReX357 のコメントのように最後の単語が切り取られることがなくなります。

これをコメントとして追加するのに十分な担当者ポイントがありません。

ほぼ希望どおりの機能を持っています。いくつか編集すれば、正確に適合します。

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

これが私がやった方法です:

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

これが古いことはわかっていますが、...

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

以前これを使用しました

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

@Dave のアイデアを使用して、substr に似た関数を作成します。

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

追伸:全長カットはsubstrよりも短い場合があります。

以下のコードに 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] . ' &bull; &bull; &bull;';

特殊文字を使用してテキストを分割し、切り取っています。

これはうまくいくと思います:

関数 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;

}

バッファーを使用すると、返される文字列の長さを調整できます。

これを使って:

次のコードは「,」を削除します。他の文字または部分文字列がある場合は、「,」の代わりにそれを使用できます。

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

// 別の文字列アカウントがある場合

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

ここでこれを試すことができます

substr( $str, 0, strpos($str, ' ', 200) ); 

これは誰かに役立つかもしれません:

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

?>
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top