题目重点:单次匹配原模板,明确占位符语法与未知键处理,避免替换值再次被解释及正则replacement转义问题。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练占位符语法为{ASCII_identifier},identifier以字母/下划线开头,后续字母、数字或下划线。
- 只对原模板做一轮替换;未知键保留原token,替换值必须是字符串,空字符串合法。
- 多余右花括号作为普通文本保留,不擅自修正模板;不支持嵌套语法/转义花括号,这些需另定合同。
解题路线
- “替换内容重复”有两种含义:同一token出现多次,或替换值也含token。先规定一轮替换,再把所有原文本匹配交给回调处理,防止递归求值的循环。
- 使用匹配回调返回params[key],而不是把它作为正则replacement格式串。这样反斜杠、美元符号或组引用形式仍是字面值,不意外触发另一层替换语法。
- 不能按参数键循环调用replace;key顺序会影响替换值中的占位符。例如name={x}、x=Jack,循环替换可能输出Jack,而单轮应输出{x}。
- 未知键保留token使信息损失最小,但不是唯一合理政策:正式接口可要求报错或空串。区别必须在输入输出合同体现,不能靠默认字典访问抛KeyError决定产品行为。
- 题目中的多余右括号保留,所以输出为Hello, my name is Jack.}。若要求模板严格校验,应先完整parse再替换;部分替换后才发现错误容易留下不可回滚的半成品。
- 替换只是字符串生成,放进HTML、SQL或命令行时仍需对应语境的编码/参数化。比如HTML文本应作为文本节点插入,不能因为占位符来源于字典就宣称内容已安全。
手动走一遍
text={name} meets {name}; {missing},params.name={x}、params.x=Jack,一轮结果{x} meets {x}; {missing}。name为空串时两个匹配都变空;{bad-key}不属于声明的identifier语法,因此保持原文。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
"""Single-pass {ASCII_identifier} replacement; unknown keys stay unchanged.
Replacement values are strings, never recursively interpreted as templates.
"""
import re
_PATTERN = re.compile(r"\{([A-Za-z_][A-Za-z0-9_]*)\}")
def replace_placeholders(text, params):
def replace(match):
key = match.group(1)
if key not in params:
return match.group(0)
value = params[key]
if not isinstance(value, str):
raise ValueError("replacement must be a string")
return value
return _PATTERN.sub(replace, text)
复杂度
此固定正则只匹配线性扫描的标识符结构;时间 O(模板长度+输出长度),空间 O(输出长度)。替换值很大时输出可能远大于模板,不能仅按输入长度估计资源。
易错点与追问
容易失分的边界
- 只替换第一个匹配。
- 参数循环导致依赖key顺序。
- 替换值被当组引用或再次递归解析。
- 静默删除未知键/多余括号。
继续深挖
- 如果要求递归替换怎样检测参数依赖环?
- 需要{{转义或嵌套语法如何设计parser?
- 输出长度上限怎样执行?
- JS实现如何保持回调和自有属性查询语义?
