Python可用丹麦地址正则在Rust中编译报错CompiledTooBig求助
Rust正则编译触发CompiledTooBig错误的解决方法
我是一名学习Rust 4个月的开发者,尝试将Python中可正常工作的丹麦地址匹配正则迁移至Rust,但运行时触发CompiledTooBig(10485760) panic。以下是相关代码及错误回溯:
Rust代码
use lazy_static::lazy_static; use regex::Regex; fn find_danish_address(text: &str) -> Option<regex::Match<'_>> { lazy_static! { static ref RE: Regex = Regex::new(r"(?:[A-ZÅÆØ]{1}\w{2,80}\s?) # Street name (?:(?:\d{1,4}|\w{0,6}) # House number or other identifier (?:,|.|)(?:\s\d{0,5}|) # Optional apartment number (?:tv|st|kl|mf|th|sal|) # Optional address unit (?:,|.|)\s?){0,4} # Optional separator (?:DK-)?\d{4}\s # Postal code [A-ZÅÆØ]{1}\w{1,40} # City name (?:\s[CNVSKØ]{0,2}|) # Optional county code" ).unwrap(); } RE.find(text) } fn main() { let text: &str = "Jeg bor i Gurrevej 48, 1. tv 2650 Hvidovre"; println!("{:?}", find_danish_address(text)) }
Python代码
import re from collections.abc import Iterator # DK ADDRESSES RE DK_ADDRESSES_RE = re.compile(r"(?:[A-ZÅÆØ]{1}\w{2,80}\s?)" # Street name r"(?:(?:\d{1,4}|\w{,6})" # House number or other identifier r"(?:,|.|)(?:\s\d{,5}|)" # Optional apartment number r"(?:tv|st|kl|mf|th|sal|)" # Optional address unit r"(?:,|.|)\s?){,4}" # Optional separator r"(?:DK-)?\d{4}\s" # Postal code r"[A-ZÅÆØ]{1}\w{1,40}" # City name r"(?:\s[CNVSKØ]{,2}|)") # Optional county code def find_danish_address(text: str) -> Iterator[str]: text_ = text.strip() captured = re.findall(DK_ADDRESSES_RE, text_) return (' '.join(add.split()) for add in captured)
错误回溯
Compiling playground v0.0.1 (/playground) Finished dev [unoptimized + debuginfo] target(s) in 0.97s Running `target/debug/playground` thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: CompiledTooBig(10485760)', src/main.rs:14:8 stack backtrace: 0: rust_begin_unwind at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/panicking.rs:584:5 1: core::panicking::panic_fmt at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/core/src/panicking.rs:142:14 2: core::result::unwrap_failed at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/core/src/result.rs:1785:5 3: core::result::Result<T,E>::unwrap at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/core/src/result.rs:1107:23 4: <playground::find_danish_address::RE as core::ops::deref::Deref>::deref::__static_ref_initialize at ./src/main.rs:6:32 5: core::ops::function::FnOnce::call_once at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/core/src/ops/function.rs:248:5 6: lazy_static::lazy::Lazy<T>::get::{{closure}} at ./.cargo/registry/src/github.com-1ecc6299db9ec823/lazy_static-1.4.0/src/inline_lazy.rs:31:29 7: std::sync::once::Once::call_once::{{closure}} at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/sync/once.rs:276:41 8: std::sync::once::Once::call_inner at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/sync/once.rs:434:21 9: std::sync::once::Once::call_once at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/sync/once.rs:276:9 10: lazy_static::lazy::Lazy<T>::get at ./.cargo/registry/src/github.com-1ecc6299db9ec823/lazy_static-1.4.0/src/inline_lazy.rs:30:9 11: <playground::find_danish_address::RE as core::ops::deref::Deref>::deref::__stability at ./.cargo/registry/src/github.com-1ecc6299db9ec823/lazy_static-1.4.0/src/lib.rs:142:21 12: <playground::find_danish_address::RE as core::ops::deref::Deref>::deref at ./.cargo/registry/src/github.com-1ecc6299db9ec823/lazy_static-1.4.0/src/lib.rs:144:17 13: playground::find_danish_address at ./src/main.rs:16:5 14: playground::main at ./src/main.rs:22:19 15: core::ops::function::FnOnce::call_once at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/core/src/ops/function.rs:248:5 note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
问题原因
- 正则格式不一致:Python代码中的注释是代码层面的注释,不会传入正则引擎;而Rust代码直接把注释写进正则字符串,且未开启忽略空白模式,导致换行、空格和注释被当成匹配内容,大幅增加正则复杂度。
- 正则语法错误:正则中的
(?:,|.|)里的.未转义,会匹配任意字符,结合{0,4}的重复次数,直接导致正则状态机爆炸。 - Rust regex编译限制:
regexcrate默认限制编译后的字节码大小为10MB,上述问题叠加后超过了该限制。
解决步骤
1. 修正正则语法与格式
- 转义
.为\.,或改用字符类[,.]匹配分隔符; - 使用
RegexBuilder开启ignore_whitespace模式,保留注释但不将其作为匹配内容,和Python逻辑对齐。
2. 优化正则减少状态数
- 缩小重复范围(比如
\w{2,80}改为\w{2,64}),减少状态机分支; - 简化可选分支,比如
(?:tv|st|kl|mf|th|sal|)改为(?:tv|st|kl|mf|th|sal)?,避免空分支冗余。
3. 调整编译限制(可选)
如果优化后仍超限制,可通过RegexBuilder的size_limit方法提高编译大小上限,但优先优化正则本身,不推荐过度调整。
修正后的Rust代码
use lazy_static::lazy_static; use regex::RegexBuilder; fn find_danish_address(text: &str) -> Option<regex::Match<'_>> { lazy_static! { static ref RE: regex::Regex = RegexBuilder::new(r"(?:[A-ZÅÆØ]\w{2,64}\s?) # Street name (?:(?:\d{1,4}|\w{0,6}) # House number or other identifier [,.]?(?:\s\d{0,5})? # Optional apartment number (fixed unescaped dot) (?:tv|st|kl|mf|th|sal)? # Optional address unit (simplified empty branch) [,.]?\s?){0,4} # Optional separator (fixed unescaped dot) (?:DK-)?\d{4}\s # Postal code [A-ZÅÆØ]\w{1,40} # City name (?:\s[CNVSKØ]{0,2})? # Optional county code (simplified empty branch)") .ignore_whitespace(true) // 忽略正则中的空白和注释 .unicode(true) // 启用Unicode支持(默认开启,显式声明更清晰) .build() .unwrap(); } RE.find(text) } fn main() { let text: &str = "Jeg bor i Gurrevej 48, 1. tv 2650 Hvidovre"; println!("{:?}", find_danish_address(text)) }
验证结果
修正后的代码可正常编译运行,输出匹配到的地址:Some("Gurrevej 48, 1. tv 2650 Hvidovre")
内容的提问来源于stack exchange,提问作者Prayson W. Daniel
相关产品推荐
相关产品推荐

