应使用std::regex url_pattern(r"(https?://[^\s"{}|\\^[]]+)");匹配url,排除空格及html/markdown分隔符以避免截断,不加icase`亦可,因协议实际多为小写;无协议url需另设模式并防误匹配。

用正则表达式匹配 URL 的基本模式
C++11 起标准库支持 std::regex,但要注意:它对复杂 URL 模式的兼容性有限,尤其在 Windows MSVC 下默认引擎不支持 p{L} 或 Unicode 属性。实际提取时,应避免追求“完美 RFC 3986 兼容”,转而用宽松但实用的模式。
常见错误是写成 https?://S+ —— 这会吞掉末尾标点(如句号、逗号),也可能跨行或捕获到非 URL 的伪链接(比如 foo://bar)。更稳妥的是限定结尾字符集:
std::regex url_pattern(R"(https?://[^s"{}|\^`[]]+)");
说明:
-
[^s"{}|^`[]]+排除空格和 HTML/Markdown 常见分隔符,防止截断 - 不加
std::regex_constants::icase也没关系,因为协议部分大小写固定(http://和HTTP://都合法,但实际中几乎只用小写) - 若需匹配
www.example.com这类无协议 URL,得另加一条模式,且要小心误匹配(如www.123)
提取所有匹配并去重 / 去噪
std::sregex_iterator 可遍历全部匹配,但原始字符串里可能有重复 URL、带参数锚点的变体(如 example.com?a=1 和 example.com?a=2),是否去重取决于用途。
实操建议:
- 直接存入
std::vector<:string></:string>保留顺序和原始形式 - 若需去重,用
std::set<:string></:string>;但注意@#@#@#@#@#@#@#@#@#@0和@#@#@#@#@#@#@#@#@#@1是不同字符串,却指向同一资源 - 真正去噪要解析 URL:用第三方库(如
uri或cpp-httplib的辅助函数)标准化 scheme、host、path,再比较;纯标准库做不到
示例片段:
std::string text = "访问 https://cppreference.com 和 http://example.com/path?x=1,或试试 www.github.com";
std::regex pattern(R"(https?://[^s"{}|\^`[]]+)");
std::vector<:string> urls;
for (std::sregex_iterator it(text.begin(), text.end(), pattern); it != std::sregex_iterator(); ++it) {
urls.push_back(it->str());
}
// urls 现在包含两个完整 URL 字符串
</:string>
Windows 上 regex 性能差?换用 string_view + 手动扫描
MSVC 的 std::regex 实现性能较差,且不支持某些 POSIX 扩展。若文本量大(如解析几 MB 日志)、或需高频调用,手动扫描更可靠。
核心思路:找 "http://" 或 "https://" 起始位置,然后向后读取直到遇到空白或分隔符。
要点:
- 用
std::string_view避免重复构造std::string - 注意协议可能被编码(如
https%3A%2F%2F),但这种属于 URL 编码内容,不在原始提取范围内 - 手动逻辑无法处理嵌套括号或引号包裹的 URL(如
"(@#@#@#@#@#@#@#@#@#@2)"),需额外判断前后字符是否为括号/引号并跳过
简单起手版:
size_t pos = 0;
while ((pos = text.find("http://", pos)) != std::string::npos ||
(pos = text.find("https://", pos)) != std::string::npos) {
size_t end = text.find_first_of("
()"{}|\^`[]", pos);
if (end == std::string::npos) end = text.length();
urls.push_back(text.substr(pos, end - pos));
pos = end + 1;
}
别忽略编码与边界问题
URL 中可能含 UTF-8 中文域名(如 @#@#@#@#@#@#@#@#@#@3),std::regex 默认按字节匹配,不会出错,但你不能指望它识别“中文字符结束”——它只认字节范围。
更麻烦的是换行和零宽空格(u200B)等不可见分隔符,它们会让正则提前终止或漏匹配。生产环境建议:
- 提前用
std::erase清理常见零宽字符(u200B-u200D,uFEFF) - 对含换行的文本,确保正则 flag 含
std::regex_constants::extended并用[^ s]替代S(因S在某些实现中包含换行) - 如果输入来自网页 HTML,先用
libxml2或lxml(C++ 绑定)提取文本节点,比硬啃正则靠谱得多
真正难的不是写出能跑的正则,而是定义清楚:你要的“URL”到底指什么——浏览器地址栏可粘贴的?HTTP 客户端能请求的?还是仅符合某种日志格式的 token?不同目标,方案差异很大。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











