news 2026/9/30 1:19:38

正则表达式实战指南:从字符串匹配到Python与SQL Server应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
正则表达式实战指南:从字符串匹配到Python与SQL Server应用

1. 别再用一堆 if 处理字符串了:正则到底在解决什么

1.1 一次让我下定决心学正则的抓狂经历

几年前我接到一个数据处理需求,用户从 Excel 里导出的通讯录,光手机号就有好几种写法:13812345678、138 1234 5678、138-1234-5678、手机:13812345678、+8613812345678,还有一批混着区号。最开始我的思路很朴素,针对每种格式写一层replace和if判断,写完之后不仅代码又臭又长,隔一周再看根本不敢改动,因为每一处逻辑都像在"缝补丁"。

后来我换成了正则表达式,核心逻辑用一条规则就能覆盖住绝大多数格式:r'1[3-9]\d{9}'。配合re.sub把非数字全部过滤掉,再用一条正则做校验,原来需要 30 行字符串处理逻辑的事情,压缩成了 5 行以内。从那一刻起我意识到,正则不是"偶尔用一下的技巧",而是每个处理文本的开发者都必须掌握的基础能力。

1.2 正则的本质:一种"描述文本模式"的语言

很多人第一次接触正则,会把它理解成一种"匹配函数",其实不太准确。正则表达式是一种独立的、跨语言的模式描述语言——你写出一段规则,引擎拿这段规则去文本里做匹配、定位、提取、替换。

它和普通字符串函数的差别,我举个生活化的例子:replace("138-1234-5678", "-", "")像是你告诉别人"把所有横杠都换成空字符串",你得精确告诉程序改哪里;而正则r'[\s-]+'的意思是"任何一个空白字符或横杠,出现一次或多次",程序自己会去找所有符合这个描述的片段。前者是在描述"具体操作对象",后者是在描述"一类规律"。面对海量不规则文本时,能描述规律的东西,效率和通用性都远高于逐个穷举。

1.3 不是所有场景都适合正则

不过我得先泼一盆冷水:正则不是万能的。遇到以下几种情况,别硬上。

一个是解析 HTML、XML 这类嵌套结构的内容,正则处理多层嵌套标签非常吃力,写出来的规则往往长到难以维护,这时候应该去用真正的解析器,比如 Python 里的BeautifulSoup、lxml。另一个是处理需要理解语义的文本,比如判断一句话是不是"赞美",正则只能看到字符规律,看不到语义。

还有一个非常关键的判断标准:如果一条正则你写了 5 分钟还写不出来,或者写出来自己都看不懂,那就要停下来重新想方案。正则该是"简单高效"的武器,而不是"炫技表演"的道具。能用split、strip解决的问题,别为了显得高级硬套正则。

2. 语法基本功:从字符类到量词,一次把房子地基打好

2.1 元字符与字符类:先记住最常用的几个"快捷键"

正则里有很多元字符,它们有特殊含义,不是匹配字面符号本身。我把最常用、出现频率最高的整理成了下面这张表,建议一开始先把这些记牢,其他的遇到了再查。

元字符含义示例
.匹配除换行符外的任意一个字符a.c可以匹配abc、a1c、a-c
\d匹配任意一个数字,等价于[0-9]\d{4}匹配 4 位数字
\D匹配任意一个非数字字符\D+匹配连续的非数字内容
\w匹配字母、数字、下划线,等价于[A-Za-z0-9_]\w+经常用于匹配变量名
\W匹配非字母、数字、下划线的字符空格、标点都算
\s匹配空白字符,包括空格、制表符、换行\s+匹配连续的空白
\S匹配非空白字符用于提取非空内容
^匹配字符串开头^abc匹配以abc开头的内容
$匹配字符串结尾abc$匹配以abc结尾的内容
\b匹配单词边界\bcat\b可以匹配单词cat,但不会匹配category

字符类用方括号[ ]表示,它让你可以定义"一组允许出现的字符"。比如[a-zA-Z0-9]表示任意大小写字母和数字;[^0-9]表示除了数字以外的任意字符,注意这里的^放在方括号内部,表示"取反",和放在正则开头的"锚点"含义完全不同,刚入门的人特别容易搞混。

2.2 量词:控制"重复多少次"

基础元字符只能匹配一个字符,如果要匹配"连续 11 位数字",不可能写 11 个\d,这时量词就该登场了。

  • *:前面的字符出现 0 次或多次,也就是"有没有都行,有就任意次"。\d*可以匹配空字符串,也可以匹配12345。
  • +:前面的字符出现 1 次或多次。\d+要求至少要有一个数字。
  • ?:前面的字符出现 0 次或 1 次,常用于表示"可选"。
  • {n}:前面的字符恰好出现 n 次。\d{11}匹配 11 位数字。
  • {n,}:至少出现 n 次。
  • {n,m}:出现 n 到 m 次,闭区间。

组合起来能解决很多实际问题。比如要匹配一个 13 位数字号码,最简单粗暴的就是^\d{13}$;要匹配 3 位或 4 位的区号,就是0\d{2,3}。量词是正则里的"循环结构",掌握它才算真正入门。

2.3 锚点与边界:搞清楚"位置"也是匹配对象

^、$、\b这三个符号匹配的不是某个字符,而是一个"位置"。这个概念很多人一开始想不明白,我举个例子。

字符串hello world中,^匹配的是h前面的那个"空位置",$匹配的是d后面的"空位置",而\b匹配的是hello和world这两个单词的两侧边界位置。如果你写cat,它能匹配category里的前三个字符;但如果写\bcat\b,因为category中cat后面紧跟着e,没有单词边界,所以匹配不上。

理解"位置也是匹配对象"这一点,对写准确不误伤的规则非常重要。尤其在处理用户输入时,^和$能避免"包含式匹配"带来的误判。比如校验手机号时,1[3-9]\d{9}不加锚点,就会把123456789012345这串长数字里中间符合条件的段落匹配出来,这不是我们要的结果,所以必须写成^1[3-9]\d{9}$。

2.4 贪婪与惰性:默认吃到底,你需要学会"克制"

正则里量词默认是贪婪的,也就是会尽可能多地匹配内容。比如用<.+>去匹配<b>加粗</b>这段文本,它会从第一个<一直吃到最后一个>,得到<b>加粗</b>整个字符串,而不是我们期望的<b>。

解决方法是把量词改成惰性匹配:在量词后面加一个英文问号,变成<.+?>,这样它会匹配到第一个>就停下来,结果是<b>。这个细节在做 HTML 片段清理、日志数据去标签时极其重要。我在处理接口返回的错误信息时经常用<.*?>这种惰性写法去剔除标签,几乎每次都会踩到贪婪匹配的坑,所以养成习惯:拿不准该吃多少时,优先考虑惰性。

3. 分组与捕获:从"能匹配"到"能提取"的进阶一步

3.1 分组到底有什么用:匹配只是第一步,提取才是目的

正则入门阶段,多数人停留在"能匹配到就开心"的状态。但实际开发中,我们往往不仅要判断"有没有",还要把"具体哪一段"抠出来。比如日志里有一行:2025-01-15 10:30:22 ERROR user_id=1024,我想提取日期和 user_id,就需要用分组了。

正则里的( )就是分组的标志。把一个子模式放进圆括号,它就成了一个捕获组,匹配引擎会把这一组单独记住。比如(\d{4})-(\d{2})-(\d{2})匹配日期时,第一组是年份,第二组是月份,第三组是日期。提取的逻辑不再靠字符串切片,而是直接按组号去取,既干净又不容易出错。

3.2 命名分组:让代码不再依赖"组号魔法"

分组多了之后,用编号group(1)、group(2)不太直观。尤其是正则写了半年之后再维护,看到group(3)根本想不起来它对应的是什么。这时候命名分组就很有用。

Python 里写(?P<year>\d{4}),之后就能用match.group('year')取值;其他语言也都有类似语法,比如 JavaScript 里是(?<year>\d{4})。我自己的习惯是:只要一个正则有三个及以上分组,一律用命名分组,可读性提升非常明显。没人愿意去数一个复杂正则里第几个左括号是第几组。

非捕获组也是必须掌握的。有时候你加括号只是想表达优先级,并不想把内容单独存下来,比如(?:abc|def)+,用(?: )来分组,既能改变优先级,又不会往捕获组列表里塞多余的东西。这个符号用好了,正则的输出结构会清爽很多。

3.3 反向引用与交替:让规则"记住"前面匹配过的内容

反向引用是个很有意思的语法,意思是"匹配和之前某个分组完全相同的内容"。比如要匹配重复出现的单词,可以用(\w+)\s+\1,这里的\1表示"和第一组一模一样的内容",于是它可以匹配hello hello、test test这类重复词。

交替符号|表示"或",比如cat|dog匹配cat或dog。注意交替的优先级很低,cat|dog food的意思是匹配cat或dog food,而不是"cat/dog 加 food"。如果你想要的是"匹配 cat 或 dog,后面接 food",必须写成(?:cat|dog) food。

4. 实战拆解:13位数字手机号码的正则一步步写出来

4.1 先别急着写正则,把需求问清楚

热搜里有一个高频问题:"13位数字手机号码正则表达式怎么写"。针对这个需求,我得先提醒一句:要是你核对一下真实业务,会发现国内手机号标准长度是 11 位,不是 13 位。所谓"13位手机号码",绝大多数情况下是两种情况:第一种,11 位手机号前面带着国际区号 86,连起来一共 13 位,比如8613812345678;第二种,需求方自己也没想明白,只是凭感觉说要 13 位。

所以拿到需求先别急着写代码,花两分钟确认:被匹配的数据具体长什么样?是纯数字还是要兼容空格横线?要不要允许+86?这些问题没确认清楚,正则写得再漂亮也是空中楼阁。

4.2 从 11 位手机号到 13 位号码的逐步演进

假设业务确认就是要匹配 13 位、以 86 开头的国内手机号码,那我们可以一步步推。

第一步,匹配 11 位手机号。国内手机号以 1 开头,第二位目前是 3 到 9 都可以,后面 9 位任意数字,所以是:

^1[3-9]\d{9}$

第二步,允许前面带上 86。86是两位数,加上手机号共 13 位:

^86?1[3-9]\d{9}$

注意这里86?的意思是"8 后面跟一个可选的 6",会匹配86或8,这显然不对。正确的写法应该是:

^86 1[3-9]\d{9}$

或者用分组表示整个 86 可选:

^(?:86)?1[3-9]\d{9}$

第三步,如果用户可能输入+86,那负号也变成可变项:

^(?:\+?86)?1[3-9]\d{9}$

这条规则能匹配13812345678、8613812345678、+8613812345678三种常见形式,而且确保总长不超过 13 位数字。如果业务里还允许0086这种国际长途前缀,那就继续在分支里加:^(?:(?:\+?86)|(?:0086))?1[3-9]\d{9}$。需求越清楚,规则就越有针对性。

4.3 边界情况:数据里的空格、横线和混在文本里的号码

现实中很少是干干净净的一串数字。常见的做法是"先清洗,再匹配"。清洗时用\D把非数字字符全部移除,然后再跑正则校验。比如 Python 里:

import re raw = "手机:138-1234-5678(微信同号)" digits = re.sub(r"\D", "", raw) # 先去掉所有非数字 if re.fullmatch(r"(?:\+?86)?1[3-9]\d{9}", digits): print("这是一个合法的大陆手机号")

这段代码里我用re.sub(r"\D", "", raw)先把号码之外的内容全部去掉,再用re.fullmatch做整串校验。fullmatch在 Python 3.4 之后非常方便,相当于自动给正则加了^...$锚点,强烈推荐。

4.4 写完正则之后,用数据说话

正则写出来一定要用多组数据自测,别只在脑子里跑一遍。我把这种测试叫"正反用例清单"。正向用例至少要覆盖:标准 11 位号码、带 86、带 +86、带空格横线混杂的写法。反向用例要覆盖:12 位号码、以 2 开头、掺杂字母、中间有非数字字符等情况。

写个简单的测试脚本:

test_cases = [ ("13812345678", True), ("8613812345678", True), ("+8613812345678", True), ("138 1234 5678", True), # 清洗后应能通过 ("1381234567", False), # 少一位 ("23812345678", False), # 第二位非法 ("138123456789", False), # 多一位 ] pattern = re.compile(r"^(?:\+?86)?1[3-9]\d{9}$") cleaned = lambda s: re.sub(r"\D", "", s) for raw, expected in test_cases: result = bool(pattern.fullmatch(cleaned(raw))) print(f"{raw:<20} -> 预期{expected}, 实际{result}, {'PASS' if result == expected else 'FAIL'}")

每次改正则,就把这份用例重跑一遍。正则这个东西,靠"肉眼审查"不靠谱,必须靠用例说话。

5. Python 正则的完整操作手册:re 模块用得好,加班少一半

5.1 入门三件套:match、search、findall,先分清场合

Python 的re模块里最常用的三个函数,很多人刚学的时候分不清。

re.match从字符串开头开始匹配,如果开头不符合,直接返回None。它其实等价于给正则前面加了一个^,所以不要拿它做"全文搜索"。

re.search在整个字符串里搜索第一个符合条件的位置,找到了就返回一个 match 对象,没找到就返回None。日常做"判断文本里有没有某类内容"时,search才是主力。

re.findall返回所有匹配到的内容,以列表形式呈现。如果正则里没有分组,列表元素是匹配到的完整字符串;如果有分组,返回的则是每个分组的元组。这经常让新手感到意外。

举个例子,从一段文本里把所有手机号提取出来:

import re text = "张三的联系方式:13812345678,李四的备用号是8613912345678" phones = re.findall(r"(?:\+?86)?1[3-9]\d{9}", text) print(phones) # ['13812345678', '8613912345678']

如果我想把"去掉前缀后的 11 位号码"也一起提取,就可以用分组:

phones = re.findall(r"(?:\+?86)?(1[3-9]\d{9})", text) print(phones) # ['13812345678', '13912345678']

注意上面第二种写法,findall因为有捕获组,返回的就不再是完整匹配串,而是分组捕获的内容。这就是findall最坑的地方,也是最有用的地方。

5.2 finditer 与 group:处理大量匹配对象时的正确姿势

当匹配结果很多,而且你不仅需要匹配内容,还需要知道位置信息时,用finditer更合适。它返回的是一个迭代器,遍历时每次给你一个 match 对象,从这个对象上可以取分组、取起止下标。

pattern = re.compile(r"(?P<date>\d{4}-\d{2}-\d{2})\s+(?P<level>\w+)\s+user_id=(?P<uid>\d+)") log_line = "2025-01-15 10:30:22 ERROR user_id=1024, msg=timeout" m = pattern.search(log_line) if m: print(m.group("date")) # 2025-01-15 print(m.group("level")) # ERROR print(m.group("uid")) # 1024 print(m.span("date")) # (0, 10)

用group("名字")代替group(1),代码的可读性和维护性完全不一样。特别是在做日志解析、文本抽取这类场景时,正则本身就够复杂的了,再靠数字编号去取分组,不出一个月你就要重新读一遍正则数括号。

5.3 re.sub 做替换:文本清洗最常用的"手术刀"

re.sub是文本清洗的核心工具。它接收三个必填参数:正则、替换内容、原字符串。替换内容里可以用\1引用第一个分组的内容,也可以直接放一个函数做动态替换。

比如把文本里所有日期格式从2025-01-15转成2025/01/15:

text = "会议时间:2025-01-15,截止日期:2025-02-01" converted = re.sub(r"(\d{4})-(\d{2})-(\d{2})", r"\1/\2/\3", text) print(converted) # 会议时间:2025/01/15,截止日期:2025/02/01

再比如你想把文本中所有连续空白字符压缩成一个空格:

text = "a b\t\tc\nd" cleaned = re.sub(r"\s+", " ", text) print(cleaned) # a b c d

替换函数的使用场景更灵活。比如把日志里的 IP 地址做脱敏处理,保留前两段,后面用星号代替:

import re def mask_ip(match): ip = match.group(0) parts = ip.split(".") return ".".join(parts[:2]) + ".*.*" log = "来自 192.168.1.100 的请求失败" print(re.sub(r"\d+\.\d+\.\d+\.\d+", mask_ip, log)) # 来自 192.168.1.*.* 的请求失败

re.sub的回调函数接收 match 对象,返回字符串作为替换结果。用这种方式做脱敏、格式化、字段归一,非常灵活。

5.4 预编译与标志位:性能与语法的双重细节

如果一个正则需要在循环里反复用,强烈建议用re.compile预编译一次,然后复用 pattern 对象。虽然 Python 的re模块本身有缓存机制,但预编译能让代码更清晰,也能提前暴露正则里的语法错误。

常用的编译标志位有以下这些:

标志位简写作用
re.IGNORECASEre.I匹配时不区分大小写
re.MULTILINEre.M让^和$按每一行的行首行尾匹配
re.DOTALLre.S让.也能匹配换行符
re.VERBOSEre.X允许在正则里加空白和注释,增强可读性

re.VERBOSE值得多说一句。正则长到一定程度,可读性会急剧下降。用re.X模式后,你可以在正则里随意换行、加注释,引擎会忽略正则里的空白字符。比如之前那个匹配日志的正则,可以写成:

pattern = re.compile(r""" (?P<date>\d{4}-\d{2}-\d{2}) # 日期 \s+ (?P<level>\w+) # 日志级别 \s+ user_id=(?P<uid>\d+) # 用户ID """, re.VERBOSE)

这个习惯一旦养成,你是不会再愿意写那种一行几百字符的"天书"正则的。

5.5 性能问题:灾难性回溯,正则也会把程序拖垮

正则引擎做匹配时,遇到大量分支和嵌套量词,可能出现"灾难性回溯",表现就是程序突然卡住、CPU 飙升。最典型的写法是(a+)+$这种"嵌套量词",一个+套着另一个+。匹配正常文本没问题,一旦让它在不匹配的文本上失败,回溯次数会指数级增长,几秒钟就能把服务拖死。

我的经验是:写嵌套量词前三思,能拆成多个正则分批匹配就不要硬塞进一个正则;同时给re模块设置超时这类兜底手段并不容易,所以更要在设计上规避。比如校验邮箱,直接写一个相对宽松合理的版本,不要试图用一个正则覆盖全世界的邮箱格式。

6. SQL Server 怎么实现正则:从 LIKE 到 CLR 的取舍

6.1 现状:SQL Server 没有原生正则运算符

很多从 MySQL、PostgreSQL 转过来的开发者,第一时间会去找 SQL Server 里的REGEXP操作符,结果发现没有。这正是 SQL Server 的一大槽点:它原生支持LIKE、PATINDEX这种简单模式匹配,但不提供完整的正则表达式函数。

所以在 SQL Server 环境里做文本模式匹配,思路要转换一下。要么用内置的LIKE和PATINDEX应付简单场景,要么通过 SQLCLR 把 .NET 的正则能力搬进数据库,要么干脆把数据取到应用层用 Python、Java 处理完再写回。选哪种,取决于你的实际场景和数据库管控权限。

6.2 LIKE 和 PATINDEX:能处理哪些常见的简单需求

LIKE虽然能力有限,但解决"前缀匹配、后缀匹配、包含特定字符"这类需求足够了。%表示任意长度的字符,_表示单个字符,[ ]可以匹配字符集。

比如校验手机号字段是否"看起来像话",可以这样写:

SELECT phone, CASE WHEN phone LIKE '[1][3-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' THEN '合法' ELSE '非法' END AS status FROM users;

LIKE的字符类支持范围写法[3-9],所以上面这串等价于"以 1 开头,第二位是 3 到 9,后面 9 位都是数字",一共 11 位。这比LIKE '1%'精确得多,但也比正则啰嗦得多。

PATINDEX和LIKE类似,区别是它返回的是匹配位置的起始下标,可以用来判断字符串中是否存在某种模式。比如找到第一个数字出现的位置:

SELECT PATINDEX('%[0-9]%', 'abc123'); -- 返回 4

6.3 SQLCLR:把 .NET 正则请进数据库

如果你确实需要在数据库内执行正则表达式,常见的方案是 SQLCLR。简单说,就是写一个 C# 类库,用System.Text.RegularExpressions实现一个RegexMatch函数,编译成 DLL,然后用CREATE ASSEMBLY部署进 SQL Server,最后创建自定义函数供 T-SQL 调用。

核心 C# 代码大概是这样的:

public class RegexFunctions { [SqlFunction] public static bool RegexMatch(string input, string pattern) { if (input == null || pattern == null) return false; return Regex.IsMatch(input, pattern); } }

部署时要注意你是否具有对应的数据库权限。很多云数据库或托管实例默认不允许启用 CLR,这种场景下强行上 SQLCLR 不现实,更建议走应用层处理。

6.4 什么时候别让数据库做正则

我个人经验是:正则匹配尽量放在应用层,数据库只做"粗筛"。原因是正则运算消耗 CPU,数据库服务器的 CPU 资源通常更宝贵,一个全表扫描配合复杂的正则匹配,很容易把线上库压垮。如果需要用正则做校验,应该在上游数据入口就拦截掉,而不是让数据库在查询时慢慢算。

数据库端的正确姿势是:能用LIKE粗筛就粗筛,能从源头保证数据格式就保证格式,正则只是兜底和偶尔的数据清理工具。

7. 最后分享几个我这些年攒下的正则经验

7.1 转义问题永远排在坑位第一名

正则里\d、\w、\.这类写法,在不同语言里转义规则不一样。Python 字符串里\d写成r"\d"才不会被转义处理,不写r前缀的话\d会被当成普通反斜杠加字母,在 Python 里会直接报错或产生完全不同的含义。JSON 里写正则,反斜杠又要再翻一倍,变成\\d。每次换环境,先问自己一句:这个正则会被谁解析、转义几层?这个习惯能帮你避开一大半莫名其妙的匹配失败。

7.2 正则也需要注释和拆解

不要迷信"一行正则走天下"。可维护性永远是第一位的,正则也一样。长正则可以拆成多个短变量,再拼在一起,每个变量名解释自己的含义,比如用 Python 的re.VERBOSE模式或者组合变量名的方式。我甚至见过同事把正则拆成"前缀部分""号码部分""后缀部分"三段来写,配合注释,三个月之后回来看依然能秒懂。这种"不炫技"的写法,才是真正给团队减负。

7.3 用在线工具和测试用例代替"拍脑袋"

有一个好的正则调试利器能节省大量时间。我个人习惯用支持即时高亮匹配结果的在线工具,先验证思路,再落地到代码里。更重要的是维护一份针对业务数据的测试用例清单,正反用例都有。每次改动正则,跑一遍用例,比任何理论论证都可靠。

7.4 学习路径上的一些体会

如果你刚开始学正则,不要追求把所有语法一次背完。先记住元字符、字符类、量词、分组、锚点这五类,就已经能覆盖 80% 的需求了。后面遇到了像断言、非捕获组、反向引用这类进阶语法,按需去查去用,用两次自然就记住了。正则这门技术,最大的特点就是"用进废退",多处理几次真实脏数据,比看十篇教程都有用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 1:18:45

嵌入式开发是否吃青春饭?分层解析与职业护城河构建

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

作者头像 李华
网站建设 2026/9/30 1:18:35

Unity场景加载原理与跨平台实战优化指南

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

作者头像 李华
网站建设 2026/9/30 1:18:35

嵌入式开发中的Vibe Coding:边界、实践与AI辅助工作流

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

作者头像 李华
网站建设 2026/9/30 1:18:13

KVM虚拟机直挂物理硬盘分区:从virtio-blk配置到权限与迁移实战

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

作者头像 李华
网站建设 2026/9/30 1:18:00

Jupyter Notebook安装指南:Python环境、conda配置与常见问题排查

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

作者头像 李华
网站建设 2026/9/30 1:17:38

C语言内存四区详解:栈、堆、全局区、代码区原理与实战

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

作者头像 李华