用于提取 URL 组件的正则表达式

regexp to extract an URL components

我正在尝试编写正则表达式来提取 URL 组件。语法可以在这里找到:RFC 3986.

有些组件是可选的。到目前为止我有:

(.+)://((.*)@)?(.+?)(:(\d*))?/((.*)\?)?((.*)#)?(.*)

分解为:

我如何改进此正则表达式以使其在 RFC3986 中有效?

有趣的事实:这个正则表达式匹配自身。

示例URL(取自RFC):foo://example.com:8042/over/there?name=ferret#nose

编辑:我忘了转义d。现在剩下要做的就是使 host 之后的所有内容成为可选的,包括领先的 /.

如果您只转义斜线,最好也转义冒号,您的正则表达式就可以正常工作。结果是(.+)\:\/\/(.*@)?(.+?)(:(\d*))?\/((.*)\?)?((.*)#)?(.*)。这是一个简单的脚本,展示了如何使用它来过滤掉无效的 URI:

更新 根据评论,我做了以下修改:

  • 我已经添加了(\:((\d*)\/))?(\/)*。解释:
    • \:((\d*) 匹配一个冒号,然后是任何数字串。
    • 之后的 \/ 匹配一个斜杠,该斜杠应该在这串数字之后。这是因为端口不能包含任何其他字符,只能包含数字。所以在uri的port-portion中是找不到的。
    • 最后,整个 port-matching 表达式是可选的,因此 ?.
    • 最后一部分表示 existing/non-existing 端口后可以有很多斜杠或没有斜杠

最终正则表达式: (.+)\:\/\/(.*\@)?(.+?)(\:((\d*)\/))?(\/)*((.*)\?)?((.*)\#)?(.*)

const myRegEx = new RegExp("(.+)\:\/\/(.*\@)?(.+?)(\:((\d*)\/))?(\/)*((.*)\?)?((.*)\#)?(.*)", "g");

const allUris = [
  /*Valid*/ "https://me@data.www.example.com:5050/page?query=value#element", 
  /*Valid*/ "foo://example.com:8042/over/there?name=ferret#nose",
  /*Valid*/ "foo://example.com",
  /*Not valid*/ "www.example.com"];


const allowedUris = allUris.map(uri => {
  // Use the regexp to match it, then return the match
  const match = uri.match(myRegEx);
  return match;
});

console.log("Here are the valid URIs:");
console.log(allowedUris.join("\n\n")); // Should only print the first two URIs from the array.

我找到了一种更好的方法来处理这个问题,同时也不会破坏捕获组。

(\w[\w\d\+\.-]*)://(.+@)?([^:/\?#]+)(:\d+)?(/[^\?#]*)?(\?[^#]+)?(#.*)?

分解:

  • (\w[\w\d\+\.-]*):// 根据 RFC-3986 匹配有效方案。
  • (.+@)?匹配用户信息;即 @ 之前的所有内容,可选。
  • ([^:/\?#]+) 匹配主机;即,遇到 :/?# 之前的所有内容。
  • (:\d+)?匹配端口;即所有数字,可选
  • (/[^\?#]*)? 匹配路径;即 / 加上可选的每个字符,直到遇到 ?#,可选地。
  • (\?[^#]+)? 匹配查询;即 ? 加上每个字符,直到遇到 #,可选。
  • (#.*)? 匹配片段;即 # 加上后面的所有内容,可选。

编辑: 我正在使用的最终版本(添加捕获组以便更好地提取 + 丢弃 new-line 个字符):

(\w[\w\d\+\.-]*)://((.+)@)?([^:/\?#\r\n]+)(:(\d+))?(/([^\?#\r\n]*))?(\?([^#\r\n]*))?(#(.*))?

Try it online 对抗随机生成的 url。