惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Vercel News
Vercel News
博客园 - 【当耐特】
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
小众软件
小众软件
Hugging Face - Blog
Hugging Face - Blog
aimingoo的专栏
aimingoo的专栏
WordPress大学
WordPress大学
G
Google Developers Blog
博客园 - 叶小钗
大猫的无限游戏
大猫的无限游戏
P
Proofpoint News Feed
J
Java Code Geeks
U
Unit 42
云风的 BLOG
云风的 BLOG
阮一峰的网络日志
阮一峰的网络日志
N
Netflix TechBlog - Medium
宝玉的分享
宝玉的分享
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
D
Docker
V
Visual Studio Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
H
Help Net Security
V
V2EX
T
Tailwind CSS Blog

zodream梦想开源/个人编程日记

文件解析笔记-zodream梦想开源/个人编程日记 密码本开发笔记之读写与保存-zodream梦想开源/个人编程日记 SkiaSharp 把 pixel byte[] 转成 SKBitmap-zodream梦想开源/个人编程日记 nas 使用 Docker 安装 gogs-zodream梦想开源/个人编程日记 复制 android 手机中的文件到电脑-zodream梦想开源/个人编程日记 周报:寻找优质的周刊-zodream梦想开源/个人编程日记 开发日志:对Markdown的代码块新增引用来源支持-zodream梦想开源/个人编程日记 周报:怎么写技术类的教程文章-zodream梦想开源/个人编程日记 css display:flex 布局尺寸超出问题-zodream梦想开源/个人编程日记 周报:SEO优化的思考-zodream梦想开源/个人编程日记 Edge 浏览器不适用 Edge Image Viewer 打开图片 -zodream梦想开源/个人编程日记 SEO 学习笔记(一) 内容来源-zodream梦想开源/个人编程日记 PHP 实现双因素身份认证(2FA)-zodream梦想开源/个人编程日记 WPF MVVM 获取List 多选数据-zodream梦想开源/个人编程日记 Burp Suite 抓包-zodream梦想开源/个人编程日记 使用 indexnow 注意事项-zodream梦想开源/个人编程日记 Godot 使用字体图标 例如: Iconfont、FontAwesome-zodream梦想开源/个人编程日记 angular 15 对指定页面进行访问限制-zodream梦想开源/个人编程日记 CSS 使用 column-count 实现瀑布流出现内容分割的解决办法-zodream梦想开源/个人编程日记 input 确认按键事件在手机端不生效-zodream梦想开源/个人编程日记 C# 使用socket 进行通讯-zodream梦想开源/个人编程日记 Maui开发中Windows应用开启管理员权限-zodream梦想开源/个人编程日记 Maui 中自定义控件-zodream梦想开源/个人编程日记 angular 14 使用 ng-template 实现tree 结构显示-zodream梦想开源/个人编程日记 c# 动态安装和卸载dll-zodream梦想开源/个人编程日记 慎用 CompositionTarget.Rendering-zodream梦想开源/个人编程日记 c# 重写 c++ 程序笔记:数据初始化-zodream梦想开源/个人编程日记 源码编译 aseprite-zodream梦想开源/个人编程日记 记录一下字符串分隔split各语言之间的不同-zodream梦想开源/个人编程日记 c# Gzip解码无头内容-zodream梦想开源/个人编程日记
截取html-zodream梦想开源/个人编程日记
zodream · 2019-12-22 · via zodream梦想开源/个人编程日记

截取html

需求

从一段html 中截取指定长度的内容,

要求:

  1. 长度为不包括html标签的内容长度,即 innerTEXT 长度
  2. 保留相关标签,并进行闭合

代码

简单版代码


/**
 * 截取html, 标签不计入长度,自动闭合标签
 * @param string $html
 * @param int $length
 * @param string $endWith
 * @bug 本方法缺陷: 未进行严格标签判断 例如 < <gg data="<a>"
 * @example ::substr('<p>1111<div/>111<br>111<i class="444">11</i>111 55555</p>', 12)
 * @return string
 */
public static function substr(string $html, int $length, string $endWith = '...'): string {
    if ($length < 1) {
        return $endWith;
    }
    $maxLength = mb_strlen($html);
    if ($maxLength < $length) {
        return $html;
    }
    $result = '';
    $n = 0;
    $unClosedTags = [];
    $isCode = false; // 是不是HTML代码
    $isHTML = false; // 是不是HTML特殊字符,如
    $notClosedTags = ['area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'link', 'meta', 'param', 'embed', 'command', 'keygen', 'source', 'track', 'wbr'];
    $tag = '';
    for ($i = 0; $i < $maxLength; $i++) {
        $char = mb_substr($html, $i, 1);
        if ($char == '<') {
            // 进入标签
            $isCode = true;
            $tag = '';
        }
        else if ($char == '&') {
            $isHTML = true;
        }
        else if ($char == '>' && $isCode) {
            $n = $n - 1;
            $isCode = false;
            $tag = explode(' ', $tag, 2)[0];
            if (substr($tag, 0, 1) === '/') {
                // 判断是否时结束标签, 倒序找到邻近开始标签,进行移除
                for ($j = count($unClosedTags) - 1; $j >= 0; $j --) {
                    if ($tag === $unClosedTags[$j]) {
                        $unClosedTags = array_splice($unClosedTags, 0, $j - 1);
                        break;
                    }
                }
                $tag = '';
            }
            if (!empty($tag) &&
                substr($tag, strlen($tag) - 1, 1) !== '/'
                && !in_array(strtolower($tag), $notClosedTags)) {
                // 不是结束标签且不是自闭合且不是无需闭合把标签加入
                $unClosedTags[] = $tag;
            }
            $tag = '';
        }
        else if ($char == ';' && $isHTML) {
            $isHTML = false;
        }
        if ($isCode && ($tag !== '' || $char !== '<')) {
            $tag .= $char;
        }
        if (!$isCode && !$isHTML && $char !== ' ') {
            $n = $n + 1;
        }
        $result .= $char;
        if ($n >= $length) {
            break;
        }
    }
    $result .= $endWith;
    for ($j = count($unClosedTags) - 1; $j >= 0; $j --) {
        $result .= sprintf('</%s>', $unClosedTags[$j]);
    }
    return $result;
}

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778

此代码存在的问题

  1. 未对 < 进行验证是否真的为标签开始
  2. 未对标签属性值中可能出现的标记 进行过滤

相关需求

  1. 指定行的截取

转载请保留原文链接: https://zodream.cn/blog/id/91.html