Извлечь комбо хоста/порта с .net regex - часть порта необязательна

Скажем, я хочу извлечь имя хоста и номер порта из строки следующим образом:

stackoverflow.com:443

Это довольно легко. Я мог бы сделать что-то вроде этого:

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

Я не беспокоюсь о схемах протоколов или действительных именах хостов/IP-адресах или портах tcp/udp, это не важно для моего запроса.

Тем не менее, мне также нужно поддерживать один твист, который делает это выше моих знаний о регулярных выражениях - имя хоста без порта:

stackoverflow.com

Я хочу использовать одно регулярное выражение для этого, и я хочу использовать именованные группы захвата, чтобы группа хостов всегда существовала в положительном совпадении, тогда как группа портов существует тогда и только тогда, когда у нас есть двоеточие, за которым следуют несколько цифр.

Я попытался сделать положительный взгляд из моего слабого понимания этого:

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

Это близко, но двоеточие (:) включено в конце захвата хоста. Поэтому я попытался изменить хост, чтобы включить что-либо, кроме двоеточия, как это:

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

Это дает мне пустой захват хоста.

Любые предложения о том, как это сделать, т.е. сделать двоеточие и номер порта необязательными, но если они есть, включите захват номера порта и сделайте двоеточие "исчезающим"?

Изменить: Все четыре ответа, которые я получил, хорошо работают для меня, но обратите внимание на комментарии в некоторых из них. Я принял sln-ответ из-за хорошей компоновки и объяснения структуры regexp. Спасибо всем, кто ответил!

Ответы

Ответ 1

Это может быть (?<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)

Ответ 2

Я предлагаю использовать класс 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

Ответ 3

Если ваше имя хоста не содержит :, например, ipv64, попробуйте следующее:

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

Ответ 4

Попробуйте следующее:

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

Это делает всю часть номера двоеточия и номера порта необязательной группой и ловит в ней номер порта. Кроме того, я использовал знак плюса, чтобы гарантировать, что имя хоста и номер порта содержат хотя бы один символ.

Ответ 5

Вы можете использовать это:

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