这几天在研究WordPress博客系统怎么添加字数统计和阅读时间估算的功能,这两个功能可以给作者和读者提供有用的信息,作者可以根据字数统计来调整文章的长度,以满足其受众的需求,而读者可以参考阅读时间,估算需要多长时间来完整阅读一篇文章。
实现这两个功能并不难,算法和实现方式有很多种,哪一种准确才是难。闹网文章代码并不多,不过最近在研究代码,就顺便再加个代码总行数显示。
一、文章字数统计
文章字数统计有很多种方式,之前没去深入研究,估计有很多博客的算法都比较简单,闹着玩下网最早的写法是借用底下版权声明的代码,这 count_words 是 begin 主题自带的函数,直接放到标题下方可以正常显示。
echo count_words($post->post_content);
但在开发模式下提示错误,因为这里的 $post 变量没有定义,所以在调用时报错。可以修改为 get_the_content() 内置函数,安全地获取文章内容。
echo count_words(get_the_content());
二、文章预计阅读时间
本来这篇文章就可以结束了,但想加个阅读时间,测试估算时间不太准确,发现是这字数统计不太严谨。笔者对比微信的文章编辑系统,网站统计的字数比微信的字数要多一些。
// 获取全文内容
$content = get_the_content();
// 过滤HTML标签并提取纯文本
$text = strip_tags($content);
// 使用正则表达式匹配中文字符
preg_match_all('/[\x{4e00}-\x{9fa5}]/u', $text, $matches);
$cnContent = join('', $matches[0]);
// 计算中文字符数量
$cnCharacterCount = mb_strlen($cnContent, 'UTF-8');
// 使用正则表达式去除非字母和数字的字符,保留空格
$enContent = preg_replace("/[^A-Za-z0-9 ]/u", '', $text);
// 计算英文字符数量
$enCharacterCount = strlen($enContent);
// 计算空格数量
$spaceCount = substr_count($text, ' ');
// 方便查看具体数量,可删除
echo "中文字符数量: ${cnCharacterCount} 字符<br>";
echo "英文字符数量: ${enCharacterCount} 字符<br>";
echo "空格数量: ${spaceCount} 个空格<br>";
// 计算中英文总字数
echo "本文共 " . ($cnCharacterCount + $enCharacterCount) . " 个字。";
// 计算中文阅读时间(250个字/分钟)
$cnTime = ceil($cnCharacterCount / 250);
// 计算英文阅读时间(200个单词/分钟)
$enWords = str_word_count($enContent);
$enTime = ceil($enWords / 200);
// 总阅读时间
$readTime = $cnTime + $enTime;
echo " 预计${readTime}分钟";
三、文章代码总行数
又想加个代码行数的,一分钟看几行,不过代码不多。
1、代码行数换算为字数
看代码的速度和文章不一样,代码一般看行数,考虑拿行数去换算为字数,假设每行代码5个字:
// 获取全文内容
$content = get_the_content();
// 提取 code 和 pre 标签中的代码内容
$codeContent = preg_match_all('/<(?:code|pre)>(.*?)<\/(?:code|pre)>/s', $content, $matches);
$codeContent = $matches[1];
// 统计代码总行数
$totalLines = 0;
foreach ($codeContent as $code) {
$lines = count(explode("\n", $code));
$totalLines += $lines;
}
// 假设每行代码相当于5个字
$codeCount = $totalLines * 5;
// 提取中文字符串,按字数统计
$cnContent = preg_match_all('/[\x{4e00}-\x{9fa5}]/u', $content, $matches);
$cnContent = join('', $matches[0]);
$cnCount = mb_strlen($cnContent);
// 提取英文单词,使用空格分割
$enContent = preg_replace("/[^A-Za-z0-9 ]/u","", $content);
$enWords = preg_split("/\s+/",$enContent);
$enCount = count($enWords);
// 总字数统计
$wordCount = $cnCount + $enCount + $codeCount;
// 输出结果
echo " 共{$wordCount}字";
if ($codeCount > 0) {
echo " 代码{$totalLines}行\n";
}
// 总阅读时间(250个字/分钟)
$readTime = ceil($wordCount / 250);
echo " 预计${readTime}分钟";
2、代码直接算字数
用换算也不太准确,不如还是直接算吧。代码的计算方式有点复杂,下划线连在一起的该算几个字呢?
闹网是按中英文总字数,再加代码行数,计算阅读时间则是包括代码的字数。假设代码中的字以空格分隔,这并不准确,也只是算个大概。
现在最终版是写个函数,然后直接调用。
//字数代码行统计
function count_nzonex($content) {
// 移除链接、图片描述和图片链接
$content = preg_replace('/<a[^>]*>.*?<\/a>|<img[^>]* alt=["\'][^"\']*["\'][^>]*>/s', ' ', $content);
// 统计中文字符数量
preg_match_all('/[\x{4e00}-\x{9fa5}]/u', $content, $matches);
$cnCount = count($matches[0]);
// 统计英文单词数量
preg_match_all('/\b\w+\p{N}*\b/', $content, $matches);
$enCount = count($matches[0]);
// 统计连续数字字符作为一个字
preg_match_all('/\d+/', $content, $matches);
$nuCount = count($matches[0]);
// 统计代码行数和字数
preg_match_all('/<code>(.*?)<\/code>/s', $content, $matches);
$codeContent = $matches[1];
$codeLines = 0;
$codeWords = 0;
foreach ($codeContent as $code) {
$lines = count(explode("\n", $code));
$codeLines += $lines;
// 统计代码字数,假设代码中的字以空格分隔
$codeWords += str_word_count(strip_tags($code));
}
return array(
'cnCount' => $cnCount,
'enCount' => $enCount,
'nuCount' => $nuCount,
'codeLines' => $codeLines,
'codeWords' => $codeWords,
);
}
function content_readTime_nzonex($content) {
$counts = count_nzonex($content);
$cnCount = $counts['cnCount'];
$enCount = $counts['enCount'];
$nuCount = $counts['nuCount'];
$codeLines = $counts['codeLines'];
$codeWords = $counts['codeWords'];
// 计算总字数
$totalWords = $cnCount + $enCount + $nuCount + $codeWords;
$CountWords = $cnCount + $enCount + $nuCount;
// 计算总字数的阅读时间,每分钟约250字
$readTime = ceil($totalWords / 250);
//$result = "总{$totalWords}字,除代码外共{$CountWords}字";
$result = "共{$CountWords}字";
if ($codeLines > 0) {
//$result .= ",代码{$codeLines}行 共{$codeWords}字";
$result .= "+代码{$codeLines}行";
}
$result .= " 预计阅读{$readTime}分钟";
return $result;
}
这个写法似乎太复杂了,还可以再优化一下。
// 统计文章字数代码行
function count_words_nzonex($content) {
// 参数验证,确保内容不为空
if (empty($content)) {
return [
'cn' => 0,
'en' => 0,
'nu' => 0,
'codelines' => 0,
'codewords' => 0
];
}
// 提取代码内容
preg_match_all('/<code>(.*?)<\/code>/si', $content, $code_matches);
$code = $code_matches[1];
// 移除HTML标签
$content = strip_tags($content);
// 统计中文字数
preg_match_all('/[\x{4e00}-\x{9fff}]/u', $content, $cn_matches);
$cn_count = count($cn_matches[0]);
// 统计英文字符数
preg_match_all('/\w+/u', $content, $en_matches);
$en_count = count($en_matches[0]);
// 统计数字字数
preg_match_all('/\d+/u', $content, $nu_matches);
$nu_count = count($nu_matches[0]);
// 统计代码行数、字数
$code_lines = 0;
$code_words = 0; // 初始化code_words
foreach ($code as $c) {
// 逐行统计
$lines = count(explode("\n", $c)); // 分割行数
$code_lines += $lines;
$code_words += str_word_count(strip_tags($c));
}
return [
'cn' => $cn_count,
'en' => $en_count,
'nu' => $nu_count,
'codelines' => $code_lines,
'codewords' => $code_words
];
}
// 计算阅读时间
function content_readTime_nzonex($content) {
// 统计字数
$counts = count_words_nzonex($content);
$cn_count = $counts['cn'];
$en_count = $counts['en'];
$nu_count = $counts['nu'];
$code_lines = $counts['codelines'];
$code_words = $counts['codewords'];
//总字数
$total_words = $cn_count + $en_count + $nu_count + $code_words;
//除代码外的字数
$text_words = $cn_count + $en_count + $nu_count;
$readtime = ceil($total_words / 250);
$result = "共{$text_words}字";
if ($code_lines > 0) {
$result .= "+{$code_lines}行代码";
}
$result .= " 预计阅读{$readtime}分钟";
return $result;
}
这样可以在别处调用这个函数,如下:
echo content_readTime_nzonex(get_the_content());
就会输出结果,可以参考本文标题下方的信息。
感觉又学到了一点皮毛。