如何在超过 170 个字符的第一个 space 之后添加省略号超链接?

How to add an ellipsis hyperlink after the first space beyond 170 characters?

我有一个长文本如下:

$postText="It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy.";

我想在 170 个字符后添加 readmore 超链接 不截断一个单词并包含尾随空白字符

我的编码尝试:

if(strlen($postText)>170){
    $splitArr=preg_split("/.{170}\S*\s/",$postText,2);
    print_r($splitArr);
    exit;
    $postText=$splitArr[0]."...<a class='see-more' href='http://example.com/seemore-link'>read more</a>";
}

拆分数组总是 return 第一个索引为 null。我在 REGEX101 中检查了我的正则表达式,它准确地显示了我需要的内容。不对的地方请指出

不需要使用preg_split you still can trim the characters with substr

$postText="It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy.";
$limit = 170;
$truncated = substr($postText,0,$limit);
$truncated .= "...<a class='see-more' href='http://example.com/seemore-link'>read more</a>";
var_dump($truncated);

Demo

为什么 preg_split() return 第一个元素是空字符串?

这是因为您为函数提供的模式决定了它应该在哪里 explode/break。匹配的字符被视为 "delimiter",实际上,使用函数的默认行为将其丢弃。

当您的输入字符串至少有 170 个字符时,然后是可选的 non-whitespace 个字符,然后是一个白色 space 字符——所有这些匹配的字符都将成为分隔符。当 preg_split() 拆分字符串时,它可能会根据分隔符的位置生成 zero-length 个元素。

例如,如果您有一个字符串 aa 并将其拆分为 a,该函数将 return 3 个空元素 - 第一个 a 之前的一个,一个在 a 之间,一个在第二个 a.

之后

代码:(Demo)

$string = "aa";
var_export(preg_split('/a/', $string));
// output: array ( 0 => '', 1 => '', 2 => '', )

为了保证不产生空字符串,可以将函数的第四个参数设置为PREG_SPLIT_NO_EMPTY(第3个参数必须声明,第4个参数才能被识别)

var_export(preg_split('/a/', $string, -1, PREG_SPLIT_NO_EMPTY));
// output: array ( )

可以 PREG_SPLIT_NO_EMPTY 参数添加到您的函数调用中以删除空字符串,但是因为您要保留的子字符串用作分隔符,它在这个过程中丢失了。


一个更重要的问题是 preg_split() 不是这项工作的最佳工具。

您发布的片段:

  1. 检查字符串是否符合截断条件
  2. 然后它会尝试隔离文本的前导部分
  3. 然后打算用包含前导部分的元素覆盖 $postText 并连接省略号超链接。

幸运的是,php 有一个函数可以在没有条件的情况下执行所有这三个步骤——从而产生干净、直接的代码行。

代码:(Demo)

$postText = "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy.";
$ellipsis = "...<a class='see-more' href='http://example.com/seemore-link'>read more</a>";
echo preg_replace('/.{170}\S*\s\K.+/', $ellipsis, $postText);

这个调用的美妙之处在于,如果 $postText 不符合截断条件,因为它没有 170 个字符,可以选择后跟 non-whitespace 个字符,然后是白色 space 字符,那么什么也不会发生——字符串保持完整。

模式中的\K命令前~170个字符是released/forgotten/discarded作为匹配字符。然后 .+ 表示 匹配一个或多个任意字符(尽可能)。按照这种模式逻辑,只会执行一次替换。 preg_replace() 修改 $postText 字符串,没有任何连接语法。

*注意,如果您的输入字符串可能包含换行符,您应该添加 s 模式修饰符,以便 . 匹配 包括换行符在内的任何字符。模式:/.{170}\S*\s\K.+/s

*如果你想在超过第 170 个字符的单词末尾截断你的输入字符串,你可以使用这个模式:/.{170}\S*\K.+/ 并且你可以在开头添加一个 space replacement/ellipsis 字符串提供一些分隔。


使用 non-regex 方法有点笨拙,并且需要条件语句来保持相同的准确性水平(因此我不推荐它,但无论如何我都会展示该技术)。

使用 substr_replace(),您需要检查字符串中是否有足够的长度来为 strpos() 提供有效的偏移量。如果是这样,你可以更换。

代码:(Demo)

$postText = "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy.";
$ellipsis = "...<a class='see-more' href='http://example.com/seemore-link'>read more</a>";
if (($len = strlen($postText)) > 170 && ($pos = strpos($postText, ' ', 170)) && ++$pos < $len){
    $postText = substr_replace($postText, $ellipsis, $pos);
}
echo $postText;

上面的代码片段假定输入字符串中只有 spaces(相对于您可能想要拆分的制表符和换行符)。

split array always return the first index as null.

没有 return NULL, it returns an empty string ('');它们是具有不同语义的完全不同的对象。

returned数组第一个元素为空字符串的原因在preg_split()的手册页中有明确记载:

Return Values:

Returns an array containing substrings of subject split along boundaries matched by pattern, or FALSE on failure.

您作为 preg_split() is used to match the delimiter, not the pieces. The function you need is preg_match() 的第一个参数提供的正则表达式:

$postText = "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy.";

preg_match('/^.{170}\S*/', $postText, $matches);

$postText = $matches[0] . " ...<a class='see-more' href='http://example.com/seemore-link'>read more</a>";

如果preg_match() returns TRUE, $matches[0]包含您需要的字符串。

有些情况下 preg_match() 无法使用原始 regex。例如,如果您的输入字符串恰好有 170 个字符,则 \s 将不匹配。这就是为什么我从 regex 中删除 \s 并在匹配后附加的字符串前面添加白色 space 的原因。

您的正则表达式 .{170}\S*\s 没问题,但有一点问题。它不保证 \S* 是否匹配单词的其余部分,因为它可能匹配 MD5 - 170 个字符到 MD5 哈希的第一个字符,然后再匹配 31 个字符,可能比这更多。

您将这 170 个字符视为 preg_split 的分隔符,因此您在输出中没有它。

考虑到这两点,您可能会想到一个更好的主意:

$array = preg_split('~^[\s\S]{1,170}+(?(?!\S{10,})\S*)\K~', $string);

PHP live demo

10 确保没有超过 non-whitespace 个字符。如果存在,它会在 170 个字符后立即拆分。

访问 $array[0] 您可以向其中添加阅读更多文本。