新闻详情

新闻详情

首页 / 资讯中心 / 详情

XML DTD元素解析实战:DOCTYPE声明、内容模型与验证

发布时间:2026/10/2 10:34:46来源:尧图网络
XML DTD元素解析实战:DOCTYPE声明、内容模型与验证
第一次在XML文件头部撞见!DOCTYPE声明时我整个人是懵的。那时候我刚搞完一个简单的接口对接对方传过来的XML长这样?xml version1.0 encodingUTF-8? !DOCTYPE catalog SYSTEM ../dtd/catalog.dtd catalog product idP1001 name机械键盘/name price currencyCNY399/price /product /catalog看到中间那行!DOCTYPE我心里犯嘀咕这东西到底是干什么的删掉会不会有问题留着他又是给谁看的后来做了一段时间的数据交换和配置文件解析才慢慢意识到DTD其实是XML里最容易被忽略、又最容易“暗算”人的一环。它管着“XML里能出现哪些元素、元素里能放什么、子元素按什么顺序出现、出现几次”。说白了DTD就是XML的合同和图纸。这篇文章不为讲理论我想直接把DTD元素解析这件事拆开揉碎结合解析器的工作机制、几种元素声明形态、内容模型的运算符以及一个完整的实战案例把整个链路捋清楚。如果你跟我当初一样对着!DOCTYPE发过愁这篇应该能帮你省不少时间。1. 解析器先看DTD不是闲得慌DOCTYPE的语法位置和解析流程1.1 DOCTYPE声明到底长什么样先看最基础的一句。XML文档头部常这样写?xml version1.0 encodingUTF-8? !DOCTYPE catalog SYSTEM catalog.dtd catalog.../catalog这一行可以拆成几个部分!DOCTYPE声明的起始标记固定写法catalog根元素的名称必须和文档实际根元素完全一致SYSTEM catalog.dtd说明DTD文件在哪里。SYSTEM表示“我自己指定一个路径”后面跟的是URI本地文件或网络地址都可以。还有一种写法是PUBLIC -//组织名//DTD类型//语言用于公开的标准DTD比如HTML就有公开的DTD标识。也就是说DTD可以放在外部独立文件里可以嵌在XML内部也可以内外部结合。内部子集的写法长这样?xml version1.0 encodingUTF-8? !DOCTYPE catalog [ !ELEMENT catalog (product*) !ELEMENT product (name, price) !ELEMENT name (#PCDATA) !ELEMENT price (#PCDATA) ] catalog.../catalog区分内外部很简单!DOCTYPE ... [中间放了声明就是内部子集。外部子集则是通过SYSTEM或PUBLIC引用一份.dtd文件。内部子集通常优先级更高解析器也会先处理内部声明这一点在处理既有通用DTD又有局部特殊需求时需要注意别让两边的同名元素声明打架。1.2 解析器拿到文件后的三步动作XML解析不是一上来就构建DOM树的。我习惯把它拆成三个阶段理解这样排查问题会快很多。第一步词法扫描。解析器先读字符流识别出?xml ?声明、标签、属性、注释、CDATA块。这个阶段如果文件连“基本语法”都不成立解析直接失败。第二步结构检查。这个时候!DOCTYPE的作用就来了。如果解析器处于“验证模式”它会根据DTD中的声明逐一对文档里的元素进行核对这个标签允许出现在这里吗它的子元素符合声明吗顺序对吗次数对吗属性有没有被定义第三步语义构建。把通过检查的内容组装成DOM树、SAX事件流或其他模型交给上层业务使用。有一个关键点值得说清楚不是所有解析器默认都会校验DTD。Java里用SAX默认是不开启验证的必须手动设置验证特性XML解析器才会去比对DTD。很多老项目里XML解析“明明写了DOCTYPE却像没写一样”就是因为解析器根本没开验证。等到生产环境换了一个开了验证的解析器瞬间报出一堆“元素内容不合法”的错这时候才回头找DTD已经晚了。所以第一步确认解析环境是否开启了验证比查DTD定义本身还重要。1.3 验证模式与非验证模式的对业务影响非验证模式下解析器主要做良构性检查也就是“XML是不是一份语法正确的文档”。这时候DTD基本派不上什么用场。验证模式下DTD就成了硬约束。比如我在需求里写过“订单必须至少包含一个明细”用DTD表达就是订单 (订单头, 明细)。如果对方传了个没有明细的订单验证模式会立刻报错从根上掐断了脏数据进入业务逻辑的可能。所以在接口对接的时候DTD不是一个“可选装饰”它实际上是一个在线校验规则。你甚至可以写一个小工具在报文入库前先用DTD过一遍把不合规的数据直接挡在门外。后面第四章我会给一个完整样例照着用就行。2. 元素声明的五种形态和它们对应的现实约束DTD里最核心的语法是!ELEMENT。一个元素声明本质上是回答一个问题这个元素里面到底允许放什么DTD把答案分成了五种套路搞懂它们DTD就算入门了一半。2.1 EMPTY空元素只存在本身!ELEMENT br EMPTY !ELEMENT image EMPTYEMPTY表示这个元素不允许有任何内容没有文本没有子元素。在XML里通常写成自闭合标签br/或image/。我第一反应想到的是HTML里的br和img。在XML里如果业务需要表达这种“占位符型”数据比如一个无需内容的标记节点EMPTY就是最干净的选择。写非自闭合的image/image其实也符合EMPTY约束因为里面没有内容但行业习惯是用自闭合。日常开发中真正用EMPTY做核心业务元素的场景不多它更多出现在框架配置、占位符定义的DTD里。2.2 ANY什么都行但尽量别用!ELEMENT description ANYANY的意思是这个元素内部可以是任意解析字符数据PCDATA也可以是任意DTD中声明过的子元素。看起来很方便实际上它相当于“放弃约束”。这里有个必须强调的坑如果DTD里声明了!ELEMENT description ANY那description里出现的子元素必须是在整个DTD中有过!ELEMENT声明的。不是真的“随便放”。但凡是声明过的元素顺序和次数都不再受当前父元素的限制这就会造成业务规则无法闭环。我在项目里见到的用法大多出现在“扩展预留位”上。比如某条记录将来可能挂载不同类型的扩展信息早期版本先用ANY占着。但我会明确建议能不用就不用。一旦二方系统上线后续你想收紧约束对方可能已经往里面塞了五花八门的内容迁移成本很高。最好的习惯是开始就定义清楚子元素列表宁可多写几个声明也不要用ANY偷懒。2.3#PCDATA文本节点!ELEMENT name (#PCDATA)#PCDATA的完整称呼是Parsed Character Data也就是“文本内容”。它意味着该元素内部只能放文本不能包含任何子元素。注释和空白除外但注释是给文档看的不参与数据模型。这是最常用的一种形态。比如商品名称、用户昵称、订单备注只要数据本身是纯文本就对应着(#PCDATA)。实际解析时DTD不会细分“这段文本是不是非空”“长度有没有限制”“是不是数字”等等。它只保证“这里有文本就行”。想要更强的类型约束DTD做不到得换XSD或者依赖业务代码校验。2.4 子元素列表结构树的骨架!ELEMENT product (name, price, stock)这个声明是XML结构设计的核心。它表示product元素内部必须按顺序出现name、price、stock这三个子元素不能多不能少顺序不能乱。一旦解析器发现顺序不符比如先遇到price再遇到name验证模式下会直接报错。我当初写XML结构时养成一个习惯先用DTD来设计结构再写XML数据。因为XML的层次结构一复杂肉眼很容易看错层级但DTD里一个括号就能把“谁在谁里面、按什么顺序”表达得明明白白。2.5 混合内容结构化文本的妥协方案!ELEMENT paragraph (#PCDATA | bold | italic)*混合内容允许文本和子元素混着出现语法只有一种固定模式(#PCDATA | 子元素名 | ...)*。注意子元素必须写在#PCDATA后面且整体必须以*结尾。这个形态非常适合描述“带格式的文本”。比如新闻正文里可能夹着bold加粗、link链接等行内元素。如果整段塞(#PCDATA)就没有办法表达“这段里有几个加粗标签”如果只用子元素列表又没法表达自由文本。混合内容把两端都照顾到了。使用中有一个容易踩的坑不要试图用(#PCDATA | bold)或(#PCDATA | bold)?这种变体。DTD对混合内容的语法卡得很死必须严格写成(#PCDATA | name)*形式很多解析器遇到不符合固定模式的写法会直接当成“内容模型无效”报错。下面这张表可以快速查阅五种形态声明形态允许的内容典型用途需要特别注意的事EMPTY无内容占位标记、空节点最好用自闭合标签ANY任意已声明元素或文本扩展预留位约束太弱容易失控(#PCDATA)纯文本名称、描述、备注不做文本长度和类型限定(子元素列表)指定子元素组合业务对象容器顺序和次数都会被校验(#PCDATA|子元素)*文本和行内元素混排富文本、正文内容语法必须固定在*结尾3. 从集合映射到业务流程子元素序列、判断分支与次数控制!ELEMENT后面的括号里绝不是把几个元素名随便一写。它实际上是一套微型的表达式语言包含了顺序、分支、重复三个维度。这三个维度拆开来看每一部分都不难但组合起来可以表达非常复杂的业务规则。3.1 三个运算符把DTD变成了约束引擎顺序用逗号表达比如(a, b, c)就是“先a再b再c”。分支用竖线表达(a | b)就是“这里只能是a或b二选一”。重复用三个符号表达?出现零次或一次*出现零次或多次出现一次或多次。这三个符号的含义和正则表达式非常像。我经常拿正则做类比因为两者虽然是完全不同的体系但在“描述重复次数”这件事上思维方式一致。学完DTD的内容模型再去看JSON Schema的required列表、XSD的sequence和choice会发现它们是同一套抽象思想的不同表达。3.2 几个典型内容模型和它们对应的规则先说最简单的固定结构!ELEMENT person (name, age, email)规则person必须有且仅有name、age、email三个子元素顺序固定。这是“每条记录必须完整、缺一不可”的典型表达。哪来的address?之类的可选字段那就得加问号!ELEMENT person (name, age, email, address?)address?表示这个字段可有可无。但如果前面三个字段也有的是可选的呢只能说DTD还不够“聪明”它不支持“只允许出现其中的某些”这种松散约束。遇到这种情况我的建议是重新审视接口契约把必选逻辑收紧。否则会出现一堆符合DTD但业务无法处理的半截数据。再看重复列表!ELEMENT order (orderHeader, orderLine)意思是order必须有且仅有一个orderHeader至少有一个orderLine而且所有orderLine都按顺序紧跟在header后面。这在订单类业务里非常常见一个订单头对应一到多个明细行。如果订单允许没有明细就改成orderLine*如果明细行最多只能有一行就改成orderLine?。分支选择可以用在“多选一”的场景比如!ELEMENT payment (cash | card | transfer)它表示一笔支付只能是现金、银行卡、转账中的一种。实际解析时如果节点里出现了两种支付方式验证直接失败。3.3 括号嵌套和组合复杂结构的正确打开方式当业务规则丰富起来单个层级的序列或选择就不够用了。DTD允许括号嵌套让子模型变成一个整体单元!ELEMENT order (orderHeader, (orderLine | orderRemark)*, orderFooter?)这个表达的意思是除去必有的orderHeader之后中间部分可以是零到多组内容每组内容要么是一条orderLine要么是一条orderRemark最后可选一个orderFooter。对这种嵌套模型我最常犯的错就是把“若干个可选元素”看得太简单。举个例子想表达“电话和邮箱至少填一个”新手容易写!ELEMENT contact (phone?, email?)这样写只能保证“phone和email都是可选的”但没法表达“至少要有一个”。也就是说contact/contact这样的空节点也能通过DTD校验。但这是业务上不该允许的。要表达“至少填一个”正确的设计是用选择加重复!ELEMENT contact (phone | email)表示“括号里的内容出现至少一次”而每次出现要么是phone要么是email。contactphone//contact能通过contactemail//contact能通过contact/contact就过不去。这就是内容模型和业务规则完美对齐的例子。类似这种“至少一个”“必须且仅有一个”“零到多个里头选一”的需求在写DTD前先想清楚对应运算符后续省下大量无效沟通。3.4 解析器报错时我怎么定位内容模型问题把这段单独列出来是因为内容模型相关的报错信息往往很有迷惑性。SAX解析器常见的报错是“元素类型为XXX的内容必须匹配...”或者“The content of element type ... must match”后面会跟着一段正则化的内容模型格式跟DTD里的括号几乎一样。遇到这种报错我的定位顺序是固定的先看消息内容模型里括号的顺序然后数一数XML里子元素的实际顺序。比如内容模型是(name, price, stock)但XML里写的是(price, name, stock)那就是顺序错。如果模型是(orderLine)XML里只有一个orderLine节点且后面跟了其他不允许的标签那多半是重复或多余内容的问题。有时DTD里声明了某个子元素但XML里根本没写报错会变成“缺少必须的属性”或“未声明元素”。这通常不是某一行的问题而是整棵子树都不匹配这时候我会先把XML缩进对齐对照DTD一行行查。定位过程其实不需要特别玄的技巧核心就是把“DTD的括号表达式”翻译成口语化的判断条件“这里必须有哪些”“可以有哪些”“这几个里选哪个”“重复几次”。翻译顺了错误一目了然。4. 实战拆解我用DTD构建并验证了一个产品目录结构前面三章讲的是语法和原理这一章我想用一整套完整案例带你走一遍从DTD设计、XML编写到验证报错处理的完整流程。这个案例基本还原了我工作中处理产品数据交换的场景。4.1 DTD里先定义结构底座我先定了产品目录的顶层逻辑一个目录里可以有任意多个产品每个产品必须有编号、名称、价格价格必须指定币种库存可选产品还可以挂若干张标签图。用DTD写出来就是这样?xml version1.0 encodingUTF-8? !ELEMENT catalog (product*) !ELEMENT product (id, name, price, stock?, image*) !ELEMENT id (#PCDATA) !ELEMENT name (#PCDATA) !ELEMENT price (#PCDATA) !ATTLIST price currency (CNY | USD | EUR) CNY !ELEMENT stock (#PCDATA) !ELEMENT image EMPTY !ATTLIST image src CDATA #REQUIRED注意这里混用了一个!ATTLIST声明。!ATTLIST是DTD里定义元素属性的语法比如price的currency属性取值范围只能是CNY、USD、EUR默认是CNYimage的src属性是必需的类型是CDATA。内容的含义如下catalog内只能出现product可以重复任意次product必须有id、name、price按顺序出现stock可有可无image可以没有也可以有很多个price是纯文本image是空元素必须带有src属性。这个设计几乎涵盖了绝大多数简单目录类XML的核心需求。你可以直接拿它当模板改一改生成自己项目的DTD。4.2 手写一份符合DTD的XML文档按上面DTD的设计我写了一版对应的XML?xml version1.0 encodingUTF-8? !DOCTYPE catalog SYSTEM catalog.dtd catalog product idP1001/id name机械键盘/name price currencyCNY399/price stock120/stock image src/img/p1001_1.png/ image src/img/p1001_2.png/ /product product idP1002/id name27寸显示器/name price currencyUSD249/price /product /catalog这份XML里第一个产品带了stock和两个image第二个产品没有stock也没有image两种情况都符合DTD因为stock是?image是*。我把文件保存成products.xml。4.3 用xmllint实际验证复现一次报错排查我平时常用xmllint来验证XML和DTD的匹配关系。这个工具在Windows上可以单独下载安装macOS和Linux则通常系统自带。命令行非常简单xmllint --valid --noout products.xml如果通过了它不会有任何输出退出码是0。我把其中一个产品故意改成错误顺序product name机械键盘/name idP1001/id ... /product再跑验证输出立刻变成类似这样products.xml:5: element product: validity error : Element product content does not follow the DTD, Expecting (id, name, price, stock?, image*), got (name, id, name, price, stock?, image*)这就是典型的内容模型校验错误。Expecting (id, name, price, stock?, image*)直接把DTD里的括号表达式打出来了后面的got (...)写出了解析器实际看到的子元素顺序。看到这里不用再猜对照括号一项项挪位置就行。我把节点顺序改回去发现还有一个隐患image是EMPTY元素但有人写成了带内容的形式image src/img/p1001_1.png主图/image验证时同样会报错。EMPTY就是“一个空壳”想放文本就得重新设计成(#PCDATA)或混合内容。这个例子提醒我一个元素到底是“自闭合标签”还是“能包文本的容器”在设计DTD时必须想清楚否则后面数据一进来解析器会不留情面地拒绝。4.4 把DTD验证嵌入到日常工具链里手动在命令行跑xmllint只能用于自测。实际项目中我更推荐把DTD验证做成构建流水线里的一个步骤。比如在用Java或Python做接口项目时可以在消息入库前调用对应的解析验证API把“不符合DTD”的报文直接拒掉。这里有一个容易被忽略的细节DTD文件本身如果使用了相对路径比如SYSTEM catalog.dtd那么解析器会基于XML文件所在目录去解析这个相对路径。如果XML和DTD不在同一目录需要确认是不是要写相对路径的上一级目录../dtd/catalog.dtd或者显式在代码里设置EntityResolver来映射。很多验证“神秘地失败”就是因为DTD文件根本没找到而不是XML内容有问题。另外IDE里日常查看XML时也会根据DOCTYPE自动去找DTD对XML标签进行高亮和自动补齐。如果你用IntelliJ IDEA不希望编辑器自动把XML文件格式化可以在File | Settings | Editor | File Types里调整格式化范围或者把某些不需要参与自动格式化的文件类型排除掉。遇到一个不认识的.dtd文件也最好先确认它和XML的关系再动手改内容。用记事本或者VS Code打开XML时如果遇到“看起来乱糟糟”的文本记得先检查是不是编码问题XML声明里写了UTF-8文件却保存成GBK这是另一个高频坑。5. DTD不是万能的它的边界和我现在对它的实际定位DTD作为XML老牌结构约束语言优点突出简洁、紧凑、上手快、能被主流解析器原生支持。但它也有明显的天花板想清楚了才能让我在工作中正确地选型。5.1 木桶短板类型缺失、命名空间缺席、扩展性不强第一个短板是数据类型。DTD对“文本”只有#PCDATA一种理解它无法表达“这个是数字”“这个是日期时间”“这个必须匹配手机号格式”。如果我要限制价格必须是正数、库存必须是整数DTD束手无策。而XSD里能用xs:decimal、xs:date、xs:pattern配合正则去做精细校验。第二个短板是命名空间。XML协会后来普遍用命名空间来区分不同体系的标签但DTD对命名空间的支持非常弱连“元素属于哪个命名空间”这种基础问题都处理得很别扭。如果项目需要多个XML命名空间协同基本只能转向XSD。第三个短板是扩展性。DTD没有继承、没有类型派生、没有element substitution group这类OOP式的设计工具。我维护过一个模块化的报文体系多个子模块都想复用“地址”结构用DTD只能重复写几遍只有XSD的complexType和import才能真正把公共结构抽出来复用。这四个问题的综合结果是凡是需要严格数据类型校验、命名空间隔离、大范围模块复用的场景DTD不是最优选。不要因为DTD简单就把它硬撑到大型企业级契约设计里那样会越写越痛苦。5.2 什么时候我会主动选择DTD虽然XSD、Relax NG等现代Schema技术更强大但我在以下场景中依然会主动用DTD遗留系统已经约定好了DTD修改成本远大于收益。报文结构极其简单比如只有几十行配置一套!ELEMENT和!ATTLIST就能讲清楚全部约束。第三方程序或老版本解析器不支持XSD只认DTD。需要给XML写一份“人也能快速读懂”的结构说明DTD的紧凑语法比XSD的长篇标签更直观。有一个很现实的经验在技术选型时别追求“最强”要追求“最匹配”。我在内部小工具里用DTD定义配置结构十分钟就能写完同一个需求如果非上XSD可能要折腾半下午。简单问题的答案不该是重型框架。5.3 迁移到XSD的心得DTD不是白学后来我陆续把几个项目的DTD迁移到了XSD最大的体会是先学DTD再学XSD反而比直接学XSD更顺。XSD里最核心的“sequence选择器”“choice选择器”“minOccurs和maxOccurs”几乎就是DTD中逗号、竖线、星号问号加号的翻版。比如DTD的(orderLine*)移到XSD就是xs:element nameorderLine type... minOccurs0 maxOccursunbounded/。理解了DTD的重复符号再看XSD的minOccurs/maxOccurs一点都不陌生。XSD新增的类型系统、命名空间、派生和替换组都是在DTD那套骨架上的增强。所以我并不建议直接从XSD入门。如果连“元素内容模型”这个概念都还没建立XSD里的复杂类型、简单类型、全局元素、局部元素这些概念叠在一起很难一次消化。从DTD建立“元素之间如何嵌套、如何排序、如何重复”的直觉再升级到XSD整个学习路径平滑得多。5.4 经验总结先画业务规则再写DTD最后才写XML这几章下来比较重要的一条实操心得是顺序问题。我跟不少同事对过这个问题大家踩坑最多的原因都一样XML已经写完一大片才开始纠结DTD里要不要加?还是。顺序反了后面必然反复改。我现在的标准姿势是先用中文把业务约束写清楚。比如“每个分类下可以有零到多个商品”“每个商品必须有编号、名称、价格”“价格币种只能是人民币或美元”“商品可选的图片地址可以有多个”。再把中文换成DTD表达式。这个环节唯一要做的就是选择“逗号、竖线、问号、星号、加号”这五种记号以及确认哪些属性要用!ATTLIST。最后才写XML样例或者先写XML样例再反过来检验DTD能不能覆盖用例。我会刻意准备几份“反例XML”去测试DTD的兜底能力比如缺字段的、顺序错的、多属性的、内容超集的各来一份确保验证器真的能把它们拦下来。这种做法让我避免过几乎所有低级的“标签结构设计完整个推倒重来”的场面。如果你现在手里正拿着一个还没落地的XML接口我建议你先别急着写标签按照这个顺序把DTD理清楚后面会顺得多。回到文章开头那个问题!DOCTYPE那一行到底干什么用现在答案很明确了它是结构的设计蓝图。解析器按DTD校验XML人按DTD理解数据系统按DTD交换契约。下次再遇到拆不开的XML结构问题不妨先问自己一句它的DTD是怎么写的把DTD读懂XML的骨架也就捏在手里了。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

电商数据分析实战:用户行为洞察与活动效果评估 2026/10/2 11:26:03

电商数据分析实战:用户行为洞察与活动效果评估

这个实战案例系列写到第十篇,我打算认真聊一个运营数据分析实战项目,核心是用户行为洞察与活动效果评估。为什么这个主题值得单独写一篇?因为它几乎把所有数据基本功都串起来了:埋点核对、数据清洗、SQL加工、用户分群、转化漏斗、…

阅读更多 →
做完习题后,如何通过总结让学习效率翻倍? 2026/10/2 11:26:03

做完习题后,如何通过总结让学习效率翻倍?

先说一个我观察了很久的现象:同样是一本练习册,有人做完之后成绩稳步提升,有人做到第三本还是原地踏步。差别不在“做”这个动作上,而在做完之后那几分钟——你是在翻答案对个错,还是真的停下来做了总结。“习题与总结…

阅读更多 →
位移运算本质:比特级搬移而非数值计算 2026/10/2 11:26:03

位移运算本质:比特级搬移而非数值计算

1. 位移运算不是“移动数字”&#xff0c;而是“搬动比特”——从硬件视角重理解左移与右移 很多人第一次学C/C位运算时&#xff0c;看到 a << 2 就下意识想&#xff1a;“把a的十进制数往左挪两位&#xff0c;比如5变成500&#xff1f;”——这恰恰是最大的认知陷阱。…

阅读更多 →
OpenRig:用2020铝型材打造可维护的ITX多节点算力机架 2026/10/2 11:26:03

OpenRig:用2020铝型材打造可维护的ITX多节点算力机架

1. OpenRig 到底是个什么东西&#xff0c;为什么值得自己攒一套OpenRig 是我前前后后改了三版才定下来的一套开源模块化算力机架方案。简单来说&#xff0c;它就是用标准铝型材加上一批通用的托盘、背板和电源模组&#xff0c;拼出一个能同时塞下好几台 ITX 主板的开放式机架。…

阅读更多 →
从零开始搞AI工程:从最小闭环到完整流水线 2026/10/2 11:26:03

从零开始搞AI工程:从最小闭环到完整流水线

1. 从零开始搞AI工程&#xff1a;先别急着写代码 我见过太多人一上来就抱着Transformers源码啃&#xff0c;或者直接开个GPU实例跑Stable Diffusion&#xff0c;结果三天之后连自己项目里哪里是数据、哪里是模型、哪里是推理服务都说不清楚。这个“ai-engineering-from-scratch…

阅读更多 →
蓝耘 MaaS 的「思考成本」怎么算?我写了个剖析器,把 6 个模型的 Token 账本翻了个遍 2026/10/2 11:25:56

蓝耘 MaaS 的「思考成本」怎么算?我写了个剖析器,把 6 个模型的 Token 账本翻了个遍

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉