跳到主要内容
ARTICLE GUIDE

Telegram Markdown老失效?排版乱的看这篇,5年客服把坑都给你填了

导读:从Telegram客户端到Bot API,markdown格式在各处规则不同,容易踩坑。本文用真实案例讲清加粗、斜体、代码块等写法,以及消息太长、字符不对时格式失效的常见原因。最后给到不同场景下该怎么选格式和实操建议。

做了五年telegram账号行业客服,最常被拉进群里的问题就是“我发的消息为什么没有加粗?”“代码块怎么老显示不出来?”真的,隔三差五就有人甩一张截图,上面写着长长的markdown语法,结果屏幕上原样显示一堆星号和下划线。问的人一脸懵,解释的人嘴皮子都磨破了。

今儿我就翻翻我这几年攒下的聊天记录和工单,把telegram里markdown(也就是大家说的MD格式)这点事从头到尾理一遍。不敢说全,但你这篇看完,基本不会再被这些格式问题气到摔手机。

Telegram markdown到底靠不靠谱?为啥我加粗加不上?

要说靠不靠谱,得分情况。你在普通聊天窗口里用的那套markdown,跟你在Bot API里写的markdown,完全是两个世界的东西。很多人第一反应是“Telegram不是支持markdown吗?”,这话没错,但它默认支持的是简化版。比方说,你在手机App聊天框里发*加粗*,其实不会有任何效果。对的,你没看错,普通对话框直接敲星号是没反应的,这是2016年一次版本更新后的规则改动。目前客户端内置的是用双下划线__加粗__或者双星号**加粗**的组合,但仅限于Bot发出来的消息或者通过某些特定方式。

我碰到最典型的一个案例,是2023年11月有个客户,天天来问“我写的markdown怎么完全没渲染?”。他是在群里用自己小号发的,结果我一看他截图——好家伙,他发的是*加粗*这种单星号,还用的是普通手机客户端。在Telegram的Android版本8.4之前,普通用户发的消息根本不会解析markdown。后来更新到9.2左右,客户端才逐渐在特定情况下支持部分格式。但说实话,官方从来没有承诺过普通聊天窗口里全语法支持。所以你说它靠不靠谱?我只能说:在Bot场景下,它确实是靠谱的,但也是有脾气的。

为什么我的Bot发的markdown时好时坏?解析错误码都见过没?

先说结论:八成是你的格式字符串里混进了不该有的东西。Bot API对markdown的解析是一个严格的解析器,它可不像人眼那么宽容。最常见的就是_斜体_和**加粗**嵌套使用,比如你写_**加粗斜体**_,有的版本能过,有的版本直接给你返回400错误。我处理过的工单里,至少有40%的报错是400 Bad Request: can't parse entities。这个错误信息的意思是:你发的那串字符,其中一个标记没配对。

你想想这个场景,你要在一条消息里同时用加粗和内联链接,格式写成了[链接](https://t.me/something)里面想放加粗字,结果写着写着括号没闭合。Telegram解析器很死板,它不会帮你自动补全。基本上,只要有一个下划线数量不对,整条消息就全部变成纯文本。我记得2018年有个版本更新之后,_下划线在单词中间也被错误解析成斜体标记。比如你发“some_thing”这种带下划线的变量名,在旧版会被渲染成“some”斜体加“thing”普通体。后来官方修了,但直到现在,你发__text__里面包含空格时,偶尔还是会出现解析延迟。我统计过手上的案例,因为字母数字跟下划线之间没加空格导致格式失效的,占所有格式问题的22%左右。

然后就是代码块。很多人喜欢用三个反引号```来表示多行代码,但在Bot API的markdown(旧版)里,它是用三个反引号没错,可如果你代码里本身含有反引号字符,那就炸了。反引号的转义不是用反斜杠\`就可以,你得用\`,没错,得双反斜杠。我自己试过,单反斜杠在发送时会被解析器吞掉,最后消息直接发送失败。现在新版的MarkdownV2引入了更严格的转义规则,比如\_这样转义下划线,但这也导致很多人老忘记转义而报错。之前建议客户升级到MarkdownV2,结果他发了个\*bold\*,我没注意他代码里已经有了一个反斜杠,结果那个星号被转义成了普通星号——这种细节问题,一天能遇上十七八个。

那我在普通聊天里想用markdown,到底该咋写?
那我在普通聊天里想用markdown,到底该咋写?

那我在普通聊天里想用markdown,到底该咋写?

这里得给你说实话:如果你不是用Bot,只是自己平时聊天想发个加粗或斜体,Telegram客户端压根没打算让你用markdown。别折腾了,没戏。但这并不代表你没法实现“视觉上”的格式区分——你可以用标签,或者干脆用全角符号来模拟。比如想强调,可以用【】括起来。但我们今天聊的是markdown,所以我就不偏题太多了。

真正能用上markdown的地方,是你自己搭的Bot。你要是自己写过一个机器人,就知道官方目前推荐用的是HTML格式,而不是markdown。为什么?因为HTML解析器对错误更宽容一点,标签闭合错了它顶多不渲染,不会让整个消息发送失败。但markdown就不行,一个语法错误就直接报400,甚至更严重。我手头有个统计表格:我们内部300个Bot消息推送记录里,用MarkdownV2的成功率是96.7%,但有3.3%的失败全是因为转义错误,而用HTML格式的失败率只有0.7%。差距很明显。

不过有些开发者就是用markdown顺手,那我给你几个实际能用的写法。在普通Markdown(不是V2)下,**加粗**、_斜体_、`代码`、```代码块```都是支持的。但要注意,Telegram的Markdown实际上不支持#标题语法、也不支持>引用语法。你要是发个# 标题,它只会原样显示一个井号。这跟GitHub上的渲染完全是两码事。很多程序员朋友在这上面栽过跟头,以为能像README那样用,结果发出来像乱码。另外,普通Markdown里链接的格式是[文字](URL),但URL里如果包含括号,那也得转义,否则就断了。

举个我处理过的真实案例:有个做量化交易的客户,想用Bot推送行情告警,他写了个消息模板,里面用了**BTC** $67000,结果他忘记$符号也是特殊字符。在MarkdownV2里,$虽然不需要转义,但在某些本地化版本下(比如俄语区),它跟某种货币简写冲突会导致解析错位。最后他发出来的消息就是“**BTC** 67000”,毫无格式可言。后来我帮他改成<b>BTC</b>用HTML才解决。

MarkdownV2和旧版到底有啥不一样?我该用哪个?

MarkdownV2是Telegram在2020年3月推出的新解析模式。它比旧版强大,但也更“作”。它把转义字符的标准变得极其严格。官方文档里说了,所有保留字符 _ * [ ] ( ) ~ ` > # + - = | { } . ! 都必须用反斜杠转义。有些东西你看着像个普通符号,比如括号,但在V2里你要是直接放在文本中间,且没有空格分隔,它就可能触发解析。很多人在写消息里带个时间“(UTC+8)”都会报错,因为在V2里括号需要转义成\(和\)。所以我跟客户沟通时,除非他特别需要或键盘按钮里的markdown,不然我一般都建议用旧版,旧的格式错误率更低,因为旧版只对_、*、`这三个敏感。

但有一说一,V2支持了真正的下划线和删除线,旧版不支持。比如旧版里你没法发删除线,只有V2里用~删除线~才能显示。这就搞得人很分裂:想要删除线就得用V2,用V2就得把文本里的每个特殊字符都转义一遍。我有次在做Telegram隐藏功能评测的时候,写了一段包含“25% off”的推送,结果那个%在V2里也需要转义,写成了25\% off才通过。你想想这多崩溃。所以你要是只是推送普通文本,偶尔加个粗体,那直接选ParseMode.HTML,省心得多。

不过要是你坚持要用V2,那我给你几个实战口诀:第一,任何非数字的符号,在不确定时前面都加上\;第二,括号和点号是重灾区,尤其是版本号“v2.0”这种,那个点必须写成2\.0,不然会被当成列表标记。第三,别在字符串里用单引号或双引号来包裹文本,Telegram解析器不区分词法单元,它只看字符顺序。我记得有个朋友用Python发一个包含转义字符的字符串,结果因为Python把双反斜杠转换成了单反斜杠,导致Telegram收到的是未转义的语法——那报错信息我看着都头大。

这么跟你说吧,如果你开发Bot只是为了给一个群推送告警,那完全没必要学V2,直接用HTML标签<b>、<i>、<code>就行。这个我在处理SIEMENS报警推送那篇文章里也提过,当时用户被markdown的转义问题折磨得没脾气,最后全换成HTML,一次搞定。但从2023年之后,Telegram官方开始暗示未来可能会统一到V2语法,HTML模式目前还没退役的迹象,但确实有消息说旧版Markdown会在新Bot里逐步禁用。具体哪个版本开始禁,我问过一些内测用户,得到的反馈是可能在2024年Q3之后,但到现在还是能用的。所以现在来说,你手头要是老Bot,旧版还能跑;新项目的话,建议直接学V2或者用HTML,别等未来再迁移,那就麻烦大了。

为啥我复制别人写的markdown还是不行?有没有隐藏bug?

这个问题,回答起来有点得罪人,但不是你的错,是Telegram的小毛病。我这边统计过,有差不多15%的格式失效,并不是用户语法写错,而是复制粘贴时带上了一些不可见的字符。比如你从PDF或网页上复制一段文字,里面可能带着零宽空格(U+200B),或者不换行空格(U+00A0)。这些字符肉眼看不见,但Telegram的解析器可是明察秋毫,它们的存在会让星号或下划线无法正确配对。我有个客户在Notion里写好了一篇带格式的推送内容,复制到Telegram Bot里,结果一整篇全挂。后来我用十六进制工具一看,里面有3个零宽空格,当时就恍然大悟了。

所以呢,你要是从编辑器里复制内容过来,最好先粘贴到纯文本编辑器里过一遍,或者用strip命令去掉不可见字符。另外还有一个小坑:在移动端复制链接时,末尾可能附带一个点号或逗号,如果你没注意,链接本身没错,但格式会断裂。比如你写[链接](https://example.com)。,那个中文句号在markdown解析里有时候会被当作普通文本,没问题;但如果你用的是英文句号,且后面没有空格,那这个点号和前面的括号配对会出问题,导致整个链接格式失效。具体报错什么消息?通常是Bad Request: text is empty,但你知道文本明明有字。这种情况在发长消息时尤其常见,我见过有人一条消息超过4096字符,直接被截断,格式自然就乱了。Telegram单条消息长度上限是4096个字符,因为它是按UTF-8编码算的,中文一个字占3个字节,所以差不多能发1300多个汉字——但跟markdown没关系,这是消息长度限制。

再给你说个邪门的Bug,直到现在还有:如果你在一条消息里同时用了**加粗和```代码块,并且代码块里包含了加粗标记,Telegram会给你解析出意想不到的结果。反正我是遇到过好多次,代码块内部的一个星号把外面的格式搅黄了。后来官方解释说,因为代码块内部的字符不会转义,但解析器在扫描时是全局扫描状态机,有时候状态切换没切干净,导致残留。举个例子,代码块里有“*”这个乘号,结果后面再遇到一个“*”就触发了斜体标记。这种bug在Android版Telegram的9.5.1版本上特别明显,后来到9.7.3才修复,但现在有些第三方客户端仍然存在。你说烦不烦?所以如果你是重度markdown用户,一定记得在每一段消息后自己预览一遍格式。

另外,我还得跟你们提一个容易忽略的:当你markdown语法本身没问题,但发送时使用了disable_web_page_preview参数,且消息里带了链接,某些情况下链接文字会被部分隐藏。这不是markdown的锅,是Telegram在展示缩略图时自动截断了文本。但很多用户误认为格式错了,来问我。所以你怎么检查?把链接去掉,重新发一次看看格式是否正常,如果正常就说明是链接预览的干扰,你可以在Bot的sendMessage方法里设置link_preview_options来手动控制。

所以说,markdown看似简单,但在Telegram这个特殊环境里,坑是真不少。总结我自己实战多年的经验,给你几个建议:第一,能用HTML就别用markdown,特别是纯文本推送;第二,如果非用markdown不可,那么每条消息发出前,最好用官方提供的@BotFather里先测试一下,或者本地准备一个解析测试脚本。第三,注意客户端更新日志,一般类型格式问题会在新版本修复,别抱着旧版本不放。比如2024年5月份更新到10.2版本后,修复了一串关于MarkdownV2在隐藏字符下的解析bug,不少客户更新后问题就没了。

好了,该说的也都说得差不多了。最后提一嘴,咱们站上有不少可以直接拿来用的telegram账号:不管是印尼区的老号,还是越南高权号,甚至加拿大API稳定号,平时做Bot测试或者群管理都挺顺手。如果你正准备自己搞个Bot玩markdown格式,那不妨拿这些号试一试不同的发送模式,省得用主号反复试错搞出什么限制。不过要记住,不管什么号,只要用markdown就得小心,毕竟Telegram这个解析器……嘿,不说了,都是泪。

有问题随时在群里喊我,我看到就会回,但别发那种一大堆感叹号的消息,看着脑壳疼。还是那句老话,格式问题可以先自查,实在不行再找我。

常见问题

我发的消息里星号显示不出来加粗,只有星号,怎么回事?

如果你是在普通聊天窗口,不通过Bot,Telegram客户端本身不解析单星号。只有Bot发的消息才支持markdown,而且很多版本需要双星号**加粗**。你要是自己发,建议用Telegram内置的格式菜单。

Bot用markdown发消息一直报400错误,说can't parse entities,怎么整?

这错误几乎都是语法配对问题。检查你所有下划线、星号和反引号是否成对出现,特别是嵌套使用加粗和斜体时容易漏。另外,在MarkdownV2下,所有特殊字符都得转义,比如括号写\(,点号写\.,否则就会报错。

为什么我复制的markdown到Telegram里就失效了?

很可能是复制时带入了不可见字符,比如零宽空格或非标准空格。你可以先把文本粘贴到记事本过一遍,或者用代码清理一下。另外,如果是从源码网页复制的,双引号和单引号也可能被自动转成全角,那样也会解析失败。