كيفية اقتطاع سلسلة في PHP إلى الكلمة الأقرب إلى عدد معين من الأحرف؟

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

  •  09-06-2019
  •  | 
  •  

سؤال

لدي مقتطف تعليمات برمجية مكتوب بلغة PHP يسحب كتلة نصية من قاعدة بيانات ويرسلها إلى عنصر واجهة مستخدم على صفحة ويب.يمكن أن تكون كتلة النص الأصلية عبارة عن مقالة طويلة أو جملة قصيرة أو اثنتين؛لكن بالنسبة لهذه الأداة، لا يمكنني عرض أكثر من 200 حرف على سبيل المثال.يمكنني استخدام substr() لتقطيع النص إلى 200 حرف، ولكن النتيجة ستكون القطع في منتصف الكلمات - ما أريده حقًا هو تقطيع النص في نهاية الحرف الأخير كلمة قبل 200 حرف.

هل كانت مفيدة؟

المحلول

باستخدام التفاف الكلمات وظيفة.فهو يقسم النصوص إلى أسطر متعددة بحيث يكون الحد الأقصى للعرض هو الذي حددته، مع كسر حدود الكلمات.بعد التقسيم، ما عليك سوى أخذ السطر الأول:

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

الشيء الوحيد الذي لا يتعامل معه هذا الخط هو الحالة التي يكون فيها النص نفسه أقصر من العرض المطلوب.للتعامل مع حالة الحافة هذه، يجب على المرء أن يفعل شيئًا مثل:

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

نصائح أخرى

سيعيد هذا أول 200 حرف من الكلمات:

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

وهنا لديك - طريقة موثوقة لاقتطاع أي سلسلة إلى أقرب كلمة كاملة، مع البقاء تحت الحد الأقصى لطول السلسلة.

لقد قمت بتجربة الأمثلة الأخرى المذكورة أعلاه ولم تسفر عن النتائج المرجوة.

وُلد الحل التالي عندما لاحظت وجود معلمة $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.

ضع في اعتبارك عند التقسيم على "كلمة" في أي مكان أن بعض اللغات مثل الصينية واليابانية لا تستخدم حرف مسافة لتقسيم الكلمات.أيضًا، يمكن للمستخدم الضار ببساطة إدخال نص بدون أي مسافات، أو استخدام بعض رموز Unicode المشابهة لحرف المسافة القياسي، وفي هذه الحالة قد يؤدي أي حل تستخدمه إلى عرض النص بالكامل على أي حال.قد تكون إحدى الطرق للتغلب على ذلك هي التحقق من طول السلسلة بعد تقسيمها على مسافات كالمعتاد، ثم، إذا كانت السلسلة لا تزال أعلى من حد غير طبيعي - ربما 225 حرفًا في هذه الحالة - فقم بالمضي قدمًا وتقسيمها بشكل غبي عند هذا الحد.

هناك تحذير آخر بخصوص أشياء كهذه عندما يتعلق الأمر بأحرف غير ASCII؛يمكن تفسير السلاسل التي تحتوي عليها بواسطة الدالة strlen()‎ القياسية في PHP على أنها أطول مما هي عليه بالفعل، لأن الحرف الواحد قد يأخذ بايتين أو أكثر بدلاً من بايت واحد فقط.إذا كنت تستخدم فقط الدالتين 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;
}

حالة واحدة محتملة مع هذا ...إذا كانت السلسلة لا تحتوي على أي مسافة بيضاء على الإطلاق في الأحرف $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 بعد الخامس r orrrrr مستبعد.

يتمتع.

سأستخدم الدالة preg_match للقيام بذلك، لأن ما تريده هو تعبير بسيط جدًا.

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

يعني التعبير "مطابقة أي فرعية تبدأ من بداية الطول 1-200 التي تنتهي بمساحة." والنتيجة هي نتيجة $ ، والمباراة في مباريات $.هذا يعتني بسؤالك الأصلي، والذي ينتهي على وجه التحديد في أي مساحة.إذا كنت تريد إنهاء الأمر بأسطر جديدة، فقم بتغيير التعبير العادي إلى:

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

حسنًا، لقد حصلت على نسخة أخرى من هذا بناءً على الإجابات المذكورة أعلاه ولكن مع مراعاة المزيد من الأشياء (utf-8, و &nbsp ;)، وهو أيضًا سطر يزيل الرموز المختصرة لـ WordPress التي تم التعليق عليها إذا تم استخدامها مع 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;
  }
/*
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 معلمات، ولكن الثلاثة الأولى فقط هي ذات الصلة بنا الآن.

المعلمة الأولى - نمط المعلمة الأولى هي نمط التعبيرات العادية التي سيتم تقسيم السلسلة.في حالتنا، نريد تقسيم السلسلة عبر حدود الكلمات.لذلك نستخدم فئة أحرف محددة مسبقًا \s الذي يطابق أحرف المسافة البيضاء مثل المسافة وعلامة التبويب وأحرف الإرجاع وتغذية السطر.

المعلمة الثانية - سلسلة الإدخال المعلمة الثانية هي السلسلة النصية الطويلة التي نريد تقسيمها.

المعلمة الثالثة - الحد من المعلمة الثالثة تحدد عدد الأساسيات التي يجب إرجاعها.إذا قمت بتعيين الحد ل 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;
?>

أقوم بإنشاء وظيفة أكثر تشابهًا مع 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);
}

ملاحظة.:قد يكون القطع بالطول الكامل أقل من 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