在C++中如何验证字符串是否为有效的IPv4地址?

55

我不需要验证IP地址是否可达或任何其他类似的内容。 我只想验证字符串是否采用了点分十进制(xxx.xxx.xxx.xxx)IPv4格式,其中xxx介于0和255之间。

17个回答

58
您可能需要使用inet_pton函数,它会对无效的AF参数返回-1,对无效地址返回0,对有效IP地址返回+1。该函数支持IPv4和未来的IPv6地址。如果您仍需要编写自己的IP地址处理程序,请记住标准32位十六进制数是一个有效的IP地址。并非所有IPv4地址都采用点分十进制表示法。
该函数既可验证地址,也可使您在相关套接字调用中使用同一地址。

1
谢谢回答。最终我写了一个 inet_pton 的封装。请参见我的答案,其中包含(非常简单的)实现。 - Bill the Lizard
5
根据文档,该函数针对无效的 AF 参数返回 -1,针对无效地址返回 0,针对有效 IP 地址返回 +1。 - Martin Bonner supports Monica
有趣的是,inet_pton的链接文档明确说明,inet_pton()实际上只接受点分十进制表示法。 - Alex Che

52

Boost.Asio提供了类ip::address。 如果你只想验证字符串,可以像这样使用它:

std::string ipAddress = "127.0.0.1";
boost::system::error_code ec;
boost::asio::ip::address::from_string( ipAddress, ec );
if ( ec )
    std::cerr << ec.message( ) << std::endl;

这个方法同样适用于十六进制和八进制的四元组。而且这也是一个更加便携的解决方案。


1
+1 是因为它具有可移植性并且适用于十六进制/八进制四元组。这是最好的答案。 - Zach Saw
1
@tothphu:你应该向Boost报告这个问题,这很可能是一个疏忽。 - Björn Pollex
您可能还想在返回的地址对象上调用 is_v4() 方法,以确保它不是IPv6地址。此外,似乎现在它只支持四个十进制点的精确表示。已经使用Boost 1.73和1.54进行了检查。 - Alex Che

49

我选择的解决方案是:

#include <arpa/inet.h>
// ...
bool Config::validateIpAddress(const string &ipAddress)
{
    struct sockaddr_in sa;
    int result = inet_pton(AF_INET, ipAddress.c_str(), &(sa.sin_addr));
    return result != 0;
}

这对其他回答中提到的大多数情况都有效。 它不能识别使用八进制或十六进制格式的IP地址,但这对我的应用程序来说是可以接受的。


5
为什么这个回答会被踩?它对于提问者来说绝对是可以接受的答案。 :) - Bill the Lizard
@Wagaf 这不在需求列表中。我更新了问题。 - Bill the Lizard
3
我认为测试应该是 result != 1。请参阅 http://pubs.opengroup.org/onlinepubs/009695399/functions/inet_ntop.html。 - Martin Bonner supports Monica
2
使用 #include <WS2tcpip.h> - Thiago Falcao
使用AF_INET6作为IPv6的第一个参数进行相同的调用。 - ericcurtin

17
这看起来似乎很简单,但有一些陷阱。例如,之前答案中发布的许多解决方案假设四元组是基于10进制的 - 但以零开头的四元组 必须 被视为一个8进制数字,因此以0开头并包含数字8或9的任何四元组都是无效的。也就是说,IP地址 192.168.1.010 不是 192.168.1.10 ,而实际上是 192.168.1.8 ,而且IP地址 192.168.019.14 是无效的,因为第三个四元组包含无效的8进制数字9。

我强烈鼓励您使用操作系统或编译器环境中提供的socket库中提供的函数。

编辑:(尽管这可能是隐含的)当然,您也可以有16进制四元组,比如192.168.1.0x0A表示192.168.1.10,当然您可以任意混合使用大写和小写字母,比如0xC0.0xa8.1.010表示192.168.1.8。如果您想玩得开心,请使用ping测试一些例子。这在跨平台上完全正常工作(在Linux、NetBSD和Win32上测试过)。

针对KaluSingh Gabbar的请求进行进一步编辑:例如,您可以将192.168.1.10指定为0xc0a8010a,它仍然表示一个有效的IP地址,例如:

[mihailim@home ~]$ ping 0xc0a8010a
PING 0xc0a8010a (192.168.1.10) 56(84) bytes of data.
^C
--- 0xc0a8010a ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2479ms

3
我知道有风险,但你刚刚又开了一罐新的坑。 :) - Bill the Lizard
1
推论:永远不要假设所有人都使用空格来使事物整齐排列。有些人使用零。叹气 - Mihai Limbășan
2
@moocha - 欢迎来到现实世界:虽然 IP 地址中的八进制或十六进制表示法可能是有效的,但不应该鼓励使用!对于实现支持它的功能也是如此。 - Roddy
3
Roddy,这不是现实世界运作的方式。在真实的世界中,软件健壮性被视为相当重要,尤其是涉及网络时,你应该尽可能地宽容接受输入并严格控制输出。 - Mihai Limbășan
请分享一下您对这个问题中所选答案中的这句话的看法:“请记住,标准的32位十六进制数是一个有效的IP地址,但不是点分十进制表示法。”我有些困惑,因为您在点分十进制格式中使用了八进制数?(我错过了什么吗?请有人纠正我) - Ramadheer Singh
显示剩余2条评论

8
这里有一个简单的方法。
bool IsIPAddress(std::string & ipaddr)
    {    

    StringTokenizer quads(ipaddr,".");

    if (quads.countTokens() != 4) return false;

    for (int i=0; i < 4; i++)
      {
      std::string quad = quads.nextToken();
      for (int j=0; j < quad.length(); j++
         if (!isdigit(quad[j])) return false;

      int quad = atoi(quads.GetTokenAt(i));
      if (quad < 0) || (quad > 255)) return false;
      }

    return true;
    }

1
StringTokenizer类来自哪个库? - Bill the Lizard
20
有很多可用的,或者你可以自己实现。这里有一个相当不错的,你可以下载:http://www.partow.net/programming/stringtokenizer/index.html (确保许可条款适用于你的项目)。 - Steve

7

您可以编写自己的函数,如下所示:

bool isValidIPv4(const char *IPAddress)
{
   unsigned char a,b,c,d;
   return sscanf(IPAddress,"%d.%d.%d.%d", &a, &b, &c, &d) == 4;
}

sscanf()sprintf()在某些情况下非常有用。:))


2
你还需要检查小于256的数字。 - rafi wiener

5
如果你在Windows上,你可以使用WSAStringToAddress函数,根据返回值我们可以知道传入的参数是否为有效的IP地址。从Windows 2000开始,该函数支持IPv4和IPv6。

2

我已经使用仅限于C标准库函数完成了相同的操作,虽然它不支持如上所述的八进制四元组,但这不应该是问题,我可以轻松添加那一部分并将其提供给您。作为一个初学者(学生),直到现在我才知道在您的ip地址中可能会有一个八进制数。我以为它必须是十进制。


1
完全不要难过。在我提出这个问题之前,我对这里提到的许多细节都毫不知情,包括你提到的八进制数。 - Bill the Lizard

2
如果你想自己编写代码而不是使用库,那么可以使用atoi()将字符转换为整数来测试每个数字的范围,并在“.”之间使用一些strcmp。您还可以进行一些快速检查,例如字符串的长度(应小于16个字符(不包括空终止符),点的数量等等。
但是,使用现有代码可能要容易得多。

2

Boost.Regex 是一个合适的选择。

bool validate_ip_address(const std::string& s)
{
   static const boost::regex e("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}");
   return regex_match(s, e);
}

不完全正确,因为它没有验证数字是否在有效的范围内,但这应该不难修复。 - Head Geek
实际上,在正则表达式中检查数字是否在有效范围内相当复杂。请参见Ferruccio的答案以获取已更正的正则表达式。 - Bill the Lizard
你可以使用正则表达式提取每个数字,然后检查每组数字以确保其在正确的范围内。如果您想阻止人们输入无法从外部访问的IP(例如127.0.0.1),则这可能更有用。 - Kibbee
正如在Ferrucio的正则表达式注释中所指出的那样,您还必须验证八进制和十六进制块。 - Mihai Limbășan

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