c++如何判断一个字符串是否为URL_c++正则表达式匹配URL【技巧】

2次阅读

使用std::Regex匹配url需显式写出协议头并转义特殊字符,推荐先清洗字符串并用子串查找替代正则以避免引擎缺陷和unicode/ipv6复杂性。

c++如何判断一个字符串是否为URL_c++正则表达式匹配URL【技巧】

std::regex 匹配 URL 要注意协议头和转义

标准 c++std::regex 不支持 p{L} 或 Unicode 字符类,也不能直接写 http:// 这种带斜杠的字面量而不转义——斜杠本身不是正则元字符,但容易和分隔符混淆(尤其在某些封装接口中)。实际写时,协议部分必须显式写出,且路径中的点号 .、问号 ?、等号 = 等需视上下文决定是否转义。

一个轻量但实用的判断逻辑是:以 http://https://ftp:// 开头,后面跟至少一个非空白字符。不追求 100% RFC 合规,而是防明显误判:

std::regex url_pattern(R"(^(https?|ftp)://[^s/$.?#].[^s]*)");

说明:

  • R"(...)" 原始字符串字面量,避免双反斜杠干扰
  • [^s/$.?#] 排除开头就接路径分隔符或查询符的非法情况(如 http:///
  • [^s]* 允许后续任意非空白字符,覆盖常见域名、路径、参数

空字符串、无协议字符串、含控制字符的输入会意外通过

上面的正则对 "https://""ftp:// "(末尾空格)、"http://texample.com" 都可能返回 true,因为 [^s]* 在匹配失败后仍可能让整体匹配成功(取决于引擎回溯行为)。真正健壮的判断必须前置清洗和基础检查:

立即学习C++免费学习笔记(深入)”;

  • 先用 str.empty()str.find_first_of("rntfv") != std::String::npos 排除空白和控制字符
  • 再检查前缀:str.substr(0, 7) == "http://"str.substr(0, 8) == "https://",比正则更快更可控
  • 如果允许相对 URL(如 //example.com),需单独分支处理,不能塞进同一正则

windowsstd::regex 可能崩溃或不支持 ecmascript 语法

MSVC 的旧版 STL(如 VS2015/2017)对 std::regex 实现不完整,std::regex_constants::ECMAScript 模式下某些断言或量词会抛 std::regex_error,甚至触发未定义行为。若遇到 regex_error: The complexity of an attempted match against a regular expression exceeded a pre-set limit,不是表达式写错了,是引擎限制了回溯深度。

可行方案:

  • 改用 boost::regex(稳定,支持完整 ECMAScript)
  • 或退回到简单子串查找 + 手动解析:检查是否有 ://、是否包含 @(用户信息)、是否在第一个 / 前有合法域名结构(如含点、不含空格)
  • 编译时加 /std:c++17 并确认 STL 版本 ≥ VS2019 16.10,部分问题可缓解

URL 中的中文、emoji、IPv6 地址会让正则迅速变复杂

一旦要支持 https://例子.comhttps://[2001:db8::1]/path,纯 std::regex 几乎不可维护。标准库 regex 不支持 (?i) 忽略大小写修饰符(部分实现支持,但不可移植),也不支持 IPv6 方括号嵌套的递归匹配。

此时建议:

  • std::string_view 分段提取:找 :// → 截取协议 → 找第一个 /?# → 对 host 部分单独做 DNS 名称校验(如检查点号位置、长度、字符集)
  • 对 IPv6 host,检查是否以 [ 开头、以 ] 结尾,中间内容可交由 inet_pton(AF_INET6, ...) 验证
  • 接受“能识别常见 URL”而非“能解析所有合法 URL”——浏览器本身也只做启发式判断

真正难的不是写对一个正则,而是定义清楚:你要拦住什么?放行什么?边界在哪。多数场景下,协议头 + 域名基本结构 + 无控制字符,已经够用。

text=ZqhQzanResources