it-swarm-vi.tech

Làm thế nào để cắt một chuỗi trong PHP với Lời gần nhất với một số ký tự nhất định?

Tôi có một đoạn mã được viết bằng PHP để kéo một khối văn bản từ cơ sở dữ liệu và gửi nó ra một tiện ích trên trang web. Khối văn bản gốc có thể là một bài viết dài hoặc một hoặc hai câu ngắn; nhưng đối với tiện ích này, tôi không thể hiển thị nhiều hơn 200 ký tự. Tôi có thể sử dụng chất nền () để cắt văn bản ở 200 ký tự, nhưng kết quả sẽ bị cắt ở giữa các từ-- điều tôi thực sự muốn là cắt văn bản ở cuối đoạn cuối Lời trước 200 ký tự.

170
Brian

Bằng cách sử dụng hàm wordwrap . Nó phân chia các văn bản thành nhiều dòng sao cho chiều rộng tối đa là chiều rộng bạn đã chỉ định, phá vỡ các ranh giới Word. Sau khi tách, bạn chỉ cần lấy dòng đầu tiên:

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

Một điều mà oneliner này không xử lý là trường hợp khi bản thân văn bản ngắn hơn chiều rộng mong muốn. Để xử lý trường hợp Edge này, người ta phải làm một cái gì đó như:

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

Giải pháp trên có vấn đề cắt văn bản sớm nếu nó chứa một dòng mới trước điểm cắt thực tế. Đây là một phiên bản giải quyết vấn đề này:

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

Ngoài ra, đây là lớp kiểm tra PHPUnit được sử dụng để kiểm tra việc thực hiện:

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

CHỈNH SỬA:

Các ký tự UTF8 đặc biệt như 'à' không được xử lý. Thêm 'u' vào cuối REGEX để xử lý nó:

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

214
Grey Panther

Điều này sẽ trả về 200 ký tự đầu tiên của từ:

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

Và ở đó bạn có nó - một phương pháp đáng tin cậy để cắt bất kỳ chuỗi nào cho toàn bộ Word gần nhất, trong khi vẫn ở dưới độ dài chuỗi tối đa.

Tôi đã thử các ví dụ khác ở trên và chúng không mang lại kết quả mong muốn.

42
Dave

Giải pháp sau đây được sinh ra khi tôi nhận thấy tham số $ break của wordwrap function:

chuỗi từ

Đây là giải pháp:

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

Ví dụ 1.

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

Ví dụ trên sẽ xuất ra:

This is very long string...

Ví dụ # 2.

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

Ví dụ trên sẽ xuất ra:

This is short string.
33
Sergiy Sokolenko

Hãy ghi nhớ bất cứ khi nào bạn chia tách bằng "Word" bất cứ nơi nào mà một số ngôn ngữ như tiếng Trung và tiếng Nhật không sử dụng ký tự khoảng trắng để phân chia các từ. Ngoài ra, người dùng độc hại có thể chỉ cần nhập văn bản mà không có bất kỳ khoảng trắng nào hoặc sử dụng một số Unicode giống với ký tự không gian tiêu chuẩn, trong trường hợp đó, bất kỳ giải pháp nào bạn sử dụng có thể sẽ hiển thị toàn bộ văn bản. Một cách để giải quyết vấn đề này có thể là kiểm tra độ dài chuỗi sau khi tách nó trên các khoảng trắng như bình thường, sau đó, nếu chuỗi vẫn vượt quá giới hạn bất thường - có thể là 225 ký tự trong trường hợp này - đi trước và tách nó ở mức giới hạn đó.

Thêm một lời cảnh báo với những thứ như thế này khi nói đến các ký tự không phải ASCII; các chuỗi chứa chúng có thể được giải thích bởi strlen () tiêu chuẩn của PHP là dài hơn thực tế, bởi vì một ký tự có thể mất hai hoặc nhiều byte thay vì chỉ một. Nếu bạn chỉ sử dụng các hàm strlen ()/struct () để phân tách các chuỗi, bạn có thể tách một chuỗi ở giữa một ký tự! Khi nghi ngờ, mb_strlen () / mb_substr () có thể dễ dàng hơn một chút.

9
Garrett Albright

Sử dụng strpose và chất nền:

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

Điều này sẽ cung cấp cho bạn một chuỗi cắt ở không gian đầu tiên sau 30 ký tự.

8
Lucas Oman

Đây là chức năng của tôi dựa trên phương pháp của @ 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

Ở đây bạn đi:

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

Thật đáng ngạc nhiên khi thật khó để tìm ra giải pháp hoàn hảo cho vấn đề này. Tôi chưa tìm thấy câu trả lời trên trang này mà không thất bại trong một số trường hợp (đặc biệt là nếu chuỗi chứa dòng mới hoặc tab hoặc nếu ngắt Word là bất cứ điều gì khác ngoài khoảng trắng hoặc nếu chuỗi có UTF- 8 ký tự đa nhân).

Đây là một giải pháp đơn giản mà hoạt động trong mọi trường hợp. Có những câu trả lời tương tự ở đây, nhưng công cụ sửa đổi "s" rất quan trọng nếu bạn muốn nó hoạt động với đầu vào đa dòng và công cụ sửa đổi "u" làm cho nó đánh giá chính xác các ký tự đa dòng UTF-8.

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

Một trường hợp Edge có thể xảy ra với điều này ... nếu chuỗi không có bất kỳ khoảng trắng nào trong các ký tự $ characterCount đầu tiên, nó sẽ trả về toàn bộ chuỗi. Nếu bạn thích nó buộc phải nghỉ ở $ characterCount ngay cả khi đó không phải là ranh giới Word, bạn có thể sử dụng điều này:

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

Một tùy chọn cuối cùng, nếu bạn muốn có nó thêm Ellipsis nếu nó cắt ngắn chuỗi ... 

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

Sự miêu tả:

  • ^ - bắt đầu từ đầu chuỗi
  • ([\s\S]{1,200}) - nhận từ 1 đến 200 của bất kỳ ký tự nào
  • [\s]+? - không bao gồm khoảng trắng ở cuối văn bản ngắn để chúng tôi có thể tránh Word ... thay vì Word...
  • [\s\S]+ - khớp với tất cả các nội dung khác

Các xét nghiệm:

  1. regex101.com hãy thêm vào or một vài r khác
  2. regex101.comorrrr chính xác 200 ký tự.
  3. regex101.com sau thứ năm rorrrrr bị loại trừ.

Thưởng thức.

3
hlcs

Ok vì vậy tôi đã nhận được một phiên bản khác dựa trên các câu trả lời ở trên nhưng lấy thêm nhiều thứ trong tài khoản (utf-8,\n và & nbsp;), cũng là một dòng tước mã ngắn wordpress được nhận xét nếu được sử dụng với 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);
}

Sử dụng:

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

Điều này sẽ xuất ra 10 từ đầu tiên.

Hàm preg_split được sử dụng để phân tách một chuỗi thành chuỗi con. Các ranh giới mà theo đó chuỗi sẽ được phân tách, được chỉ định bằng cách sử dụng mẫu biểu thức chính quy.

Hàm preg_split có 4 tham số, nhưng chỉ có 3 tham số đầu tiên có liên quan đến chúng tôi ngay bây giờ.

Tham số đầu tiên - Mẫu Tham số đầu tiên là mẫu biểu thức chính quy dọc theo đó chuỗi sẽ được phân tách. Trong trường hợp của chúng tôi, chúng tôi muốn chia chuỗi qua các ranh giới Word. Do đó, chúng tôi sử dụng lớp ký tự được xác định trước \s khớp với các ký tự khoảng trắng như dấu cách, tab, trả về vận chuyển và nguồn cấp dữ liệu.

Tham số thứ hai - Chuỗi đầu vào Tham số thứ hai là chuỗi văn bản dài mà chúng tôi muốn tách.

Tham số thứ ba - Giới hạn Tham số thứ ba chỉ định số lượng các chuỗi con sẽ được trả về. Nếu bạn đặt giới hạn thành n, preg_split sẽ trả về một mảng gồm n phần tử. Các phần tử n-1 đầu tiên sẽ chứa các chuỗi con. Phần tử (n th) cuối cùng sẽ chứa phần còn lại của chuỗi.

2
bodi0

Tôi sẽ sử dụng hàm preg_match để làm điều này, vì những gì bạn muốn là một biểu thức khá đơn giản.

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

Biểu thức có nghĩa là "khớp với bất kỳ chuỗi con nào bắt đầu từ đầu độ dài 1-200 kết thúc bằng dấu cách." Kết quả là kết quả $ và trận đấu là trong trận đấu $. Điều đó quan tâm đến câu hỏi ban đầu của bạn, đặc biệt kết thúc trên bất kỳ không gian nào. Nếu bạn muốn làm cho nó kết thúc trên dòng mới, hãy thay đổi biểu thức thông thường thành:

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

Đây là cách tôi đã làm nó:

$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

Tôi có một chức năng gần như những gì bạn muốn, nếu bạn thực hiện một vài chỉnh sửa, nó sẽ phù hợp chính xác:

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

Dựa trên regex của @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

Đây là một sửa chữa nhỏ cho câu trả lời của mattmac:

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

Sự khác biệt duy nhất là thêm một khoảng trắng ở cuối chuỗi $. Điều này đảm bảo Word cuối cùng không bị cắt theo nhận xét của ReX357.

Tôi không có đủ điểm rep để thêm nhận xét này.

1
tanc

Đã thêm các câu lệnh IF/ELSEIF vào mã từ DaveAmalMurali để xử lý chuỗi không có khoảng trắng

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

Tôi biết điều này đã cũ, nhưng ...

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

Tôi tạo một hàm tương tự như lớp nền và sử dụng ý tưởng của @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.: Việc cắt chiều dài đầy đủ có thể ít hơn chất nền.

0
evandro777

Tôi tin rằng đây là cách dễ nhất để làm điều đó:

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

Tôi đang sử dụng các ký tự đặc biệt để phân chia văn bản và cắt nó.

0
Namida

Tôi đã sử dụng điều này trước đây

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

Tôi tìm thấy công việc này:

hàm rút gọn_ chuỗi_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;

}

Bộ đệm cho phép bạn điều chỉnh độ dài của chuỗi trả về.

0
Mat Barnett

Dùng cái này: 

đoạn mã sau sẽ xóa ','. Nếu bạn có bất kỳ ký tự hoặc chuỗi con nào khác, bạn có thể sử dụng ký tự đó thay vì ','

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

// nếu bạn có một tài khoản chuỗi khác cho 

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