【问题标题】:Regex to match the path before the resource from a URL正则表达式匹配来自 URL 的资源之前的路径
【发布时间】:2011-09-07 10:39:57
【问题描述】:

只是为了让大家理解所涉及的词汇,URL的一般结构如下:

  http   ://   www.a.com   /  path/to/resource.html  ?  query=value  #  fragment
{scheme} ://  {authority}  /         {path}          ?   {query}     #  {fragment}

路径由路径和资源组成,对于path/to/resource.html,路径为path/to/,资源为resource.html

可怜、肮脏和野蛮:
HTML,就像它在野外发现的那样,可以是poor, nasty and brutish,,尽管通常很不短。在这个贫穷、肮脏和野蛮的世界中,实时链接碰巧存在,尽管 URL 应该遵守 the standards,但它们本身可能是贫穷、肮脏和野蛮的。因此,考虑到这一点,我向您提出问题...

问题:

我正在尝试创建一个正则表达式以从 URL 的路径中删除资源,当网页中的链接是相对路径时,这是必需的。例如:

  1. 我访问www.domain.com/path/to/page1.html
  2. 有一个到/page2.html的相对链接
  3. 从 URL 中删除 /page1.html
  4. /page2.html 附加到www.domain.com/path/to

结果:www.domain.com/path/to/page2.html

我被困在第 3 步了!

我已经隔离了路径和资源,但现在我想将两者分开。我试图想出的正则表达式如下所示:\z([^\/]\.[^\/])

在 C# 中,相同的正则表达式是:"\\z([^/]\\.[^/])"

翻译成英文,正则表达式的意思应该是:匹配字符串的结尾,其中包括由点分隔的所有字符,只要这些字符不是斜线。

我尝试了那个正则表达式,但目前它失败了。实现上述结果的正确查询是什么。

以下是一些示例:

/path/to/resource.html => /path/to/ resource.html
/pa.th/to/resource.html => /pa.th/to/ resource.html
/path/to/resource.html/ => /path/to/resource.html/
/*I#$>/78zxdc.78&(!~ => /*I#$>/ 78zxdc.78&(!~

感谢您的帮助!

【问题讨论】:

  • 您的最终样本不是合法的 URI,因为它包含许多必须转义的字符。
  • 为什么不使用System.Uri
  • @Rob 我知道这不是一个合法的 URI,但没有什么能阻止人们在他们的网页中放置此类链接。我仍然需要解析这些链接(尽管 Uri 类处理了大部分这些内容)。
  • @amit_g 我已经使用了 Uri 类,但它只提供了一个 PathAndQuery(实际上是 Path+Resource+Query),所以我删除了查询,我仍然必须删除资源。
  • @Lirik,使用 HostSegmentsQueryFragment

标签: c# regex url


【解决方案1】:

Uri 类已经为你完成了几乎所有的工作时,我无法想象你为什么要为此使用正则表达式。要获得最后一部分(即从路径中分离资源),您可以使用String.LastIndexOfString.Substring。例如:

Uri myUri;
if (!Uri.TryCreate(linkString, UriKind.RelativeOrAbsolute, out myUri))
{
    // some kind of error.
}
int pos = myUri.AbsolutePath.LastIndexOf('/');
++pos;
string resource = myUri.AbsolutePath.Substring(pos);    

我毫不怀疑你可以用正则表达式来做这些事情。我怀疑,尽管这是一场胜利。正如您所说,您在爬网时找到的网址可能非常糟糕。我的爬虫花费了相当大的精力来规范一些看起来很狂野的网址。我经常遇到http://example.com/dir/subdir/subsubdir/../../dir///moretrash/resource.html 之类的东西。而且您不会相信(或者,如果您正在爬网,您可能会相信)我看到的奇怪的转义。 Uri 类在解析 url 方面做得很好,这样我就可以对其进行规范化。 Unescaping 是你不能用正则表达式做的事情。

我的经验是,创建Uri 实例的时间与规范化 url 所需的时间相比相形见绌:取消转义、剥离片段和会话标识符、识别和避免代理和爬虫陷阱、删除无关的斜杠和路径导航(即/.//../)等等。我只是看不出在哪里使用正则表达式,即使它比Uri.TryCreate 快也能提高我的运行时间。而且我严重怀疑它在解析我在野外找到的网址时是否能像Uri.TryCreate 一样出色。

【讨论】:

  • 据我所知 System.Uri 没有 Path 属性(如果我在这里错了,请纠正我),它只有一个 PathAndQuery,我目前正在使用它......我将查询拆分出来,但留下了几种类型的路径,例如不以斜杠结尾的路径/path/to,因此您的解决方案会将/path/ 视为路径,将to 视为资源。
  • 我认为他的意思是 AbsolutePath (msdn.microsoft.com/en-us/library/system.uri.absolutepath.aspx),但如果你要这样做,那么你应该在解析中使用带有 LastIndexOf 和 Substring 的链接字符串,而不是先构造一个 URI。
  • @Brian,知道了... AbsolutePath 可能没问题,我要检查它是否真的在不包含资源的路径末尾放置了一个斜杠。如果是这样,那么我应该被设置。
  • 啊,开枪!我的意思是 AbsoluteUri (msdn.microsoft.com/en-us/library/system.uri.absoluteuri.aspx) 不是 AbsolutePath...
  • @Brian,绝对路径是我现在拥有的最接近的东西,但是当没有资源时它也不会用斜线关闭路径......所以现在我必须检查最后一个斜杠,然后检查它后面的点,使用正则表达式可能会更快。
【解决方案2】:

System.Uri

var uri = new Uri("http://www.domain.com/path/to/page1.html?query=value#fragment");

Console.WriteLine(uri.Scheme); // http
Console.WriteLine(uri.Host); // www.domain.com
Console.WriteLine(uri.AbsolutePath); // /path/to/page1.html
Console.WriteLine(uri.PathAndQuery); // /path/to/page1.html?query=value
Console.WriteLine(uri.Query); // ?query=value
Console.WriteLine(uri.Fragment); // #fragment
Console.WriteLine(uri.Segments[uri.Segments.Length - 1]); // page1.html

for (var i = 0 ; i < uri.Segments.Length ; i++)
{
    Console.WriteLine("{0}: {1}", i, uri.Segments[i]);
    /*
    Output
    0: /
    1: path/
    2: to/
    3: page1.html
    */
}

【讨论】:

  • 明白了!因此,将您的代码放在“1000 字”中:您的建议是查看最后一段,如果它包含一个点,那么我在最后一个斜杠“/”上分割绝对路径,并使用它之前的任何内容作为路径。遍历段和Concating 字符串可能有点太慢了。
  • 它不会比 RegEx 中所需的步骤慢多少,除非您要在短时间内处理数百万条路径。也没有必要连接。如果最后一段被确定为资源,则 Replace(uri.AbsolutePath, uri.Segments[uri.Segments.Length - 1], "") 就是您要查找的路径。
  • 我实际上正在尝试在短时间内处理数百万条路径...感谢您的建议和澄清。
  • @Lirik,从未进行过数百万条路径处理,所以我不知道使用 Uri 类与 RegEx 相比会慢多少(如果有的话)。建议以两种方式进行基准测试,看看是否存在性能差异以及差异有多大。不用说,与为 Url 之类的东西编写自定义 RegEx 相比,使用 Uri 之类的类(经过我的 MS 和其他人的充分测试)将提供相对更容易、更快且更不脆弱的代码。但是,性能提升可能值得花时间构建自定义解决方案。让我们知道进展如何,如果您有基准测试结果,那也很棒。
  • 我目前正在使用此解决方案每秒处理大约 100k 个 URL,因此除非有人抱怨性能,否则我不会过分关注它。
【解决方案3】:

您的正则表达式引擎是否支持可变长度的前瞻?如果是这样,您可以使用它来向前看(因此排除)末尾的非斜线字符:

.*/(?=[^/]*$)

或者,使用捕获组,路径将是 group 1,资源 group 2

(.*/)([^/]*$)

一个非正则表达式算法是这样的:

  1. 存储最后一个斜线的pos
  2. 从 0 开始的子字符串,长度为 pos+1

注意:我在这里故意忽略了.。它们有什么意义?在 HTML 中,如果您的路径不以斜线结尾,则相对路径将相对于最后一部分的父级。因此,出于本次讨论的目的,没有点的部分基本上是无扩展资源。

【讨论】:

    【解决方案4】:

    我认为也许您应该在“/”上拆分字符串,而不是坚持使用正则表达式。你也看过http://msdn.microsoft.com/en-us/library/ms952653.aspx

    【讨论】:

    • 我不能在 '/' 上拆分字符串,因为路径可以是 /path/to/resource.html 或只是 /path/to... 如果我在斜杠上拆分,那么它会将 to 视为当它实际上是路径的一部分时的资源。当然,我可以检查最后一项是否包含“。”,但我认为正则表达式在这方面可能更有效(我必须对其进行测试)。
    【解决方案5】:

    要提取 URI 的资源部分,您可以使用:

    ^                  # matches start of str
    .*                 # greedy match up to the last '/'
    \/                 # literal '/'
    (                  # start capture of resource part
       [^\/\?\#]*      # zero or more chars except '/', '?', and '#'
    )                  # end capture
    (?:                # start optional group - query part
      \?               # literal '?' for optional query
      .+?              # non-greedy match for any chars
    )?                 # end of optional group
    (?:                # start of optional group - fragment part
      \#               # literal '#' for optional fragment
      .+?              # non-greedy match for any chars
    )?                 # end of optional group
    $
    

    【讨论】:

      猜你喜欢
      • 2012-08-14
      • 2022-08-11
      • 1970-01-01
      • 2020-09-30
      • 2016-12-11
      • 1970-01-01
      • 2014-08-23
      • 2017-11-08
      • 2018-01-08
      相关资源
      最近更新 更多