使用.NET正则表达式提取主机/端口组合 - 端口部分可选

3
我想从这样的字符串中提取主机名和端口号:stackoverflow.com:443。这很容易。我可以做这样的事情:(?.*):(?\d*)。我不担心协议方案或有效的主机名/IP地址或TCP/UDP端口,这对我的请求不重要。然而,我还需要支持一个细节,这使得它超出了我对正则表达式的了解——没有端口的主机名:stackoverflow.com。我想使用单个正则表达式,并且我想使用命名捕获组,使得主机组始终存在于正匹配中,而端口组仅在我们有冒号后跟数字时存在。我尝试从我对它的肤浅理解中进行积极的回溯:(?.*)(?<=:)(?\d*)。这来得比较接近,但冒号(:)包含在主机捕获的末尾。所以我试图改变主机,让它包括除冒号之外的任何东西,就像这样:(?[^:]*) (?<=:)(?\d*)。这给了我一个空的主机捕获。有什么建议吗?即使冒号和端口号是可选的,但如果它们存在,包括端口号捕获并使冒号“消失”?

2
编辑 - 没有测试,但可以尝试例如这个:(?<host>[^:]+)(:(?<port>\d+))? 记住问号本身可以用来定义可选字符或整个组。 - Zoltán Tamási
@ZoltánTamási:确实有道理。Sln的回答似乎接近您的建议,如果您发表了答案而不是评论,我会接受的。 :) - Rune Jacobsen
2
不是真正的嵌套表达式,而是一个可选的捕获组,应该是一个可选的聚合组,特别是在计算命名捕获组和/或命名组时,它们位于较大表达式的最后。 - user557597
@ZoltánTamási:我测试过了,对我的用途很有效。sln和Sabuj的答案也是如此 - 我希望我能接受所有三个答案。:S - Rune Jacobsen
@RuneJacobsen 你肯定可以至少给这三个答案点赞 :) 无论如何,如果我的回答没有被接受,我也不介意,我很高兴能够帮助。 - Zoltán Tamási
显示剩余4条评论
5个回答

5
我建议使用Uri类代替正则表达式来处理这个问题,具体请参考Uri类
// 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上查看使用示例。


2
该问题已被添加到Stack Overflow正则表达式FAQ中,作为非正则表达式的替代方案,分类在“常见任务 > 验证”下。 - aliteralmind
没有考虑到这一点,因为我解析的数据不一定是 Uri,但当然你也可以这样做。 :) - Rune Jacobsen

2

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

 (?<host> [^:]+ )               # (1), Host, required
 (?:                            # Cluster group start, optional
      :                              # Colon ':'
      (?<port> \d+ )                 # (2), Port number
 )?                             # Cluster group end
< p > 编辑 - 如果不使用群组集合,而是将捕获组用作该群组集合,则这是 Dot-Net 在其默认配置状态下“计数”群组的方式 -

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

此答案已添加到Stack Overflow正则表达式FAQ,位于“常见验证任务”下。 - aliteralmind
1
@aliteralmind,请考虑将此答案(https://dev59.com/wX7aa4cB1Zd3GeqPlxhB#24399003)用于该常见问题解答。 - Alex Klaus

1
如果您的主机名不包含像 ipv64 这样的 :,请尝试这个方法:
(?<host>[^:]*):?(?<port>\d*)

这会匹配 "stackoverflow.com8080",对吧? - Zoltán Tamási
@ZoltánTamási 但楼主说“不担心协议方案或有效的主机名” - Sabuj Hassan
我认为主机名和端口之间的冒号比有效的主机名和协议模式低一级 :) - Zoltán Tamási
Zoltán 是正确的,它会匹配这个,但 Sabuj 也是正确的 - 对于这个正则表达式,我希望尽可能地解析这个输入,即使输入可能存在格式不正确的情况。在代码中的其他正则表达式中,我将验证并警告非法/错误的输入。 - Rune Jacobsen

1

试试这个:

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

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

1
你可以使用这个:
(?<host>[^:]+)(:(?<port>\\d+))?

这个可以工作,但是你能解释一下在d前面为什么要加两个反斜杠吗?也就是说,我知道\d代表一个数字。一个反斜杠和两个反斜杠之间的区别似乎是返回的捕获组数量。 - Rune Jacobsen
这是用于在C#字符串中转义反斜杠的。在这种情况下不应该出现它,但是在普通的C#字符串中,您必须像您所知道的那样对其进行转义。 - brz
@user3246354,正则表达式几乎总是应该使用带有at符号的verbatim字符串声明,因此您不必担心转义反斜杠。通常,正则表达式已经足够复杂了,不需要这样做。 - Zoltán Tamási
是的,那是我的错误。 - brz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接