【问题标题】:Extract host/port combo with .net regex - port part optional使用 .net 正则表达式提取主机/端口组合 - 端口部分可选
【发布时间】:2014-03-27 19:34:06
【问题描述】:

假设我想从这样的字符串中提取主机名和端口号:

stackoverflow.com:443

这很容易。我可以这样做:

(?<host>.*):(?<port>\d*)

我不担心协议方案或有效的主机名/IP 地址或 tcp/udp 端口​​,这对我的请求并不重要。

但是,我还需要支持一个超出我对正则表达式知识范围的扭曲 - 没有端口的主机名:

stackoverflow.com

我想为此使用单个正则表达式,并且我想使用命名捕获组,这样主机组将始终存在于正匹配中,而端口组存在当且仅当我们有一个冒号后跟几位数字。

我试图从我对它的微弱理解中做出积极的回顾:

(?<host>.*)(?<=:)(?<port>\d*)

这很接近,但冒号 (:) 包含在主机捕获的末尾。所以我尝试将主机更改为包含除冒号之外的任何内容:

(?<host>[^:]*)(?<=:)(?<port>\d*)

这给了我一个空主机捕获。

关于如何完成此操作的任何建议,即使冒号和端口号可选,但如果它们存在,包括端口号捕获并使冒号“消失”?

编辑:我收到的所有四个答案对我来说都很好,但要注意其中一些的 cmets。由于正则表达式结构的布局和解释很好,我接受了 sln 的回答。感谢所有回复!

【问题讨论】:

  • 已编辑 - 未测试,但请尝试以下示例: (?[^:]+)(:(?\d+))?请记住,问号本身可用于定义可选字符或整个组。
  • Jerry:我应该提到 - 这是一个更大、更复杂的正则表达式的一部分,它不仅仅做主机/端口的事情。所以我只想隔离我遇到问题的部分。
  • Zoltán:所以基本上是一个嵌套表达式?哇,这将正则表达式带到了下一个令人头疼的水平。 :) 谢谢,会试试!
  • @RuneJacobsen,是的,因为您想要一个完整的可选组(冒号后跟端口),并且想要捕获其中的数字部分,因此一个组内另一个组是有意义的。
  • 不是一个真正的嵌套表达式,一个可选的捕获组应该是一个可选的集群组,特别是如果您在较大的表达式中计算命名捕获组和/或命名组最后。

标签: c# .net regex


【解决方案1】:

我建议使用Uri class 而不是正则表达式。

// Use URI class for parsing only
var uri = new Uri("http://" + fullAddress);
// get host
host = uri.DnsSafeHost;
// get port
portNum = (ushort)uri.Port;

好处是

  • 它支持:
    • IPv4 和 IPv6
    • 国际化域名(IDN
  • 将来可以扩展以考虑架构
  • 简短且标准化的代码,减少错误

查看.NET Fiddle上的使用示例

【讨论】:

  • 此问题已添加到Stack Overflow Regular Expressions FAQ,作为非正则表达式替代方案,位于“常见任务 > 验证”下。
  • 没有考虑这一点,因为我正在解析的数据不一定是 Uri,但当然你也可以这样做是有道理的。 :)
【解决方案2】:

这可能是(?<host>[^:]+)(?::(?<port>\d+))?

 (?<host> [^:]+ )               # (1), Host, required
 (?:                            # Cluster group start, optional
      :                              # Colon ':'
      (?<port> \d+ )                 # (2), Port number
 )?                             # Cluster group end

edit - 如果您不使用集群组,而是使用捕获组作为该集群组,这就是 Dot-Net 在其默认配置状态下“计数”组的方式 -

 (?<host> [^:]+ )         #_(2), Host, required                           
 (                        # (1 start), Unnamed capture group, optional
      :                        # Colon ':'
      (?<port> \d+ )           #_(3), Port number                           
 )?                       # (1 end)

【讨论】:

【解决方案3】:

如果您的主机名不包含:(如 ipv64),那么试试这个:

(?<host>[^:]*):?(?<port>\d*)

【讨论】:

  • 这将匹配“stackoverflow.com8080”,不是吗?
  • @ZoltánTamási 但 OP 说 not worried about protocol schemes or valid host names
  • 我认为主机名和端口之间的冒号比有效的主机名和协议模式低一级:)
  • Zoltán 是对的,它会匹配这个,但 Sabuj 也是对的 - 对于这个正则表达式,我想尽可能地解析这个,因为输入可能格式不正确。在代码中其他点的其他正则表达式中,我将验证非法/错误输入并发出警告。
【解决方案4】:

试试这个:

(?<host>[^:]+)(:(?<port>\d+))?

这使得整个冒号和端口号部分成为一个可选组,并在其中捕获端口号。另外,我使用加号来确保主机名和端口号至少包含一个字符。

【讨论】:

    【解决方案5】:

    你可以用这个:

    (?<host>[^:]+)(:(?<port>\\d+))?
    

    【讨论】:

    • 这行得通,但你能解释一下 d 前面的两个反斜杠的原因吗? IE。我知道 \d 代表一个数字。一个和两个反斜杠之间的区别似乎是返回的捕获组的数量。
    • 用于转义 C# 字符串中的反斜杠。在这种情况下它不应该存在,但是在普通的 c# 字符串中,您必须如您所知将其转义。
    • @user3246354,正则表达式几乎总是应使用 at 符号逐字字符串声明,因此您无需担心转义反斜杠。通常一个正则表达式没有它也足够复杂。
    • 是的,这是我的错误。
    猜你喜欢
    • 2015-10-03
    • 1970-01-01
    • 2012-07-18
    • 2019-12-18
    • 2010-10-01
    • 2012-10-09
    • 2016-09-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多