Advice/help/better 检查给定字符串是否为有效 IP 地址的解决方案
Advice/help/better solution for checking whether a given string can be a valid IP address or not
老实说,我认为我写的代码很垃圾,而且我认为这不是解决问题的最佳方法。我需要一些建议或改进来解决这个问题。我还是编码新手。如果您能提供一些有关如何使用字符串和各种字符串函数的提示,我们将不胜感激。
字符串成为 IP 地址的条件:-
连接到互联网的设备的标识号。以点分四组表示法编写的 IPv4 地址由四个由句点分隔的 8 位整数组成。
换句话说,它是一个由四个数字组成的字符串,每个数字介于 0 和 255 之间(含 0 和 255),带有“.”。每个数字之间的字符。所有数字都应不带前导零。
示例:
- 192.168.0.1 是一个有效的 IPv4 地址
- 255.255.255.255 是一个有效的 IPv4 地址
- 280.100.92.101 不是有效的 IPv4 地址,因为 280 太大而不是 8 位整数(最大的 8 位整数是 255)
- 255.100.81.160.172 不是有效的 IPv4 地址,因为它包含 5 个整数而不是 4
- 1..0.1 不是有效的 IPv4 地址,因为它的格式不正确
- 17.233.00.131 和 17.233.01.131 不是有效的 IPv4 地址,因为它们包含前导零
这是我的代码(我知道这是垃圾而且没有任何意义):-
bool isIPv4Address(char * inputString) {
int count = 0, period = 0;
int k = 0, i = 0;
int digit = 0;
while(inputString[i] != '[=11=]')
{
count = 0;
digit = 0;
i = k;
if(inputString[i] == '0' || inputString[i] == '.')
{
if(inputString[i+1] >47 && inputString[i+1] < 58)
{
return false;
}
}
while(inputString[i] != '.' && inputString[i] != '[=11=]')
{
if(inputString[i] < 48 || inputString[i] > 57)
{
return false;
}
count += (int)inputString[i];
i++;
digit++;
}
if(digit == 3)
{
if(inputString[i - 3] > '2')
{
return false;
}
}
if(inputString[i] == '.')
{
period++;
}
k = i+1;
if(count < 48 || count > 156)
{
return false;
}
if(inputString[i] == '[=11=]')
{
break;
}
}
if(period == 3)
{
return true;
}else
{
return false;
}
}
对于 '0'
之类的东西,您有很多松散的 47、48 等值。最好使用后一种语法。
有许多 if
范围检查。使用一些额外的状态变量可以降低复杂性。
到处使用inputString[i]
很麻烦。最好这样做(例如 int chr = inputString[i];
并改用 chr
——这样更简单易读)。
原程序错误识别于:
192.168.0.1
280.100.92.101
我重构了代码并构建了诊断测试程序。注释为:
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#define ERR(_expr) \
if (err != NULL) \
break; \
if (_expr) { \
err = #_expr; \
break; \
}
const char *bigerr;
int opt_i;
bool
isfix1(const char *inputString)
{
int i = 0;
int ndig = -1;
int ndot = 0;
int val = 0;
int leading_zero = 0;
unsigned char ipv[4];
const char *err = NULL;
while (1) {
int chr = inputString[i++];
int eos = (chr == 0);
// check the last number we got
if ((chr == '.') || eos) {
ERR(ndig == 0);
// we don't allow (because it might be decoded as octal):
// x.00.y.z x.01.y.z
// but this must be ok:
// x.0.y.z
ERR(leading_zero && (ndig > 1));
// we can only have 3 dots in the string
ndot += (! eos);
ERR(ndot > 3);
ndig = 0;
val = 0;
if (eos) {
// we must have _exactly 3 dots in the string
ERR(ndot != 3);
break;
}
continue;
}
// collect digits
if ((chr >= '0') && (chr <= '9')) {
val *= 10;
chr -= '0';
val += chr;
if (ndig < 0)
ndig = 0;
// remember whether substring started with "0"
if (ndig == 0)
leading_zero = (chr == 0);
// we can only have up to 3 digits per byte
++ndig;
ERR(ndig > 3);
// max value for byte
ERR(val > 255);
// just for fun -- the binary IP address
ipv[ndot] = val;
continue;
}
// any invalid character
ERR(chr != 0);
}
if (0)
printf("IPV: %d.%d.%d.%d\n",ipv[0],ipv[1],ipv[2],ipv[3]);
bigerr = err;
return (err == NULL);
}
bool
isorig(const char *inputString)
{
int count = 0,
period = 0;
int k = 0,
i = 0;
int digit = 0;
while (inputString[i] != '[=11=]') {
count = 0;
digit = 0;
i = k;
if (inputString[i] == '0' || inputString[i] == '.') {
if (inputString[i + 1] > 47 && inputString[i + 1] < 58) {
return false;
}
}
while (inputString[i] != '.' && inputString[i] != '[=11=]') {
if (inputString[i] < 48 || inputString[i] > 57) {
return false;
}
count += (int) inputString[i];
i++;
digit++;
}
if (digit == 3) {
if (inputString[i - 3] > '2') {
return false;
}
}
if (inputString[i] == '.') {
period++;
}
k = i + 1;
if (count < 48 || count > 156) {
return false;
}
if (inputString[i] == '[=11=]') {
break;
}
}
if (period == 3) {
return true;
}
else {
return false;
}
}
int
dofnc(bool (*fnc)(const char *),const char *ipaddr,const char *reason)
{
int valid;
bigerr = NULL;
valid = fnc(ipaddr);
printf("%s",valid ? "valid" : "INVALID");
if (bigerr != NULL)
printf(" (%s)",bigerr);
printf(" (%s)\n",(valid != (reason == NULL)) ? "FAIL!" : "pass");
return valid;
}
void
dotest(const char *ipaddr,const char *reason)
{
static int sep = 0;
if (sep)
printf("\n");
sep = 1;
printf("IPADDR: %s",ipaddr);
if (reason != NULL)
printf(" -- %s",reason);
printf("\n");
int valid[2];
printf("isorig: ");
valid[0] = dofnc(isorig,ipaddr,reason);
printf("isfix1: ");
valid[1] = dofnc(isfix1,ipaddr,reason);
do {
if (opt_i)
break;
for (int idx = 0; idx < 2; ++idx) {
if (valid[idx] != (reason == NULL))
exit(1);
}
} while (0);
}
int
main(int argc,char **argv)
{
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
char *cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'i':
opt_i = ! opt_i;
break;
}
}
dotest("192.168.0.1",NULL);
dotest("255.255.255.255",NULL);
dotest("280.100.92.101","280 is too large to be an 8-bit integer");
dotest("255.100.81.160.172","contains 5 integers instead of 4");
dotest("1..0.1","not properly formatted");
dotest("1.0.1.","not properly formatted");
dotest("17.233.00.131","contain leading zeros");
dotest("17.233.01.131","contain leading zeros");
return 0;
}
程序输出如下:
IPADDR: 192.168.0.1
isorig: INVALID (FAIL!)
isfix1: valid (pass)
IPADDR: 255.255.255.255
isorig: valid (pass)
isfix1: valid (pass)
IPADDR: 280.100.92.101 -- 280 is too large to be an 8-bit integer
isorig: valid (FAIL!)
isfix1: INVALID (val > 255) (pass)
IPADDR: 255.100.81.160.172 -- contains 5 integers instead of 4
isorig: INVALID (pass)
isfix1: INVALID (ndot > 3) (pass)
IPADDR: 1..0.1 -- not properly formatted
isorig: INVALID (pass)
isfix1: INVALID (ndig == 0) (pass)
IPADDR: 1.0.1. -- not properly formatted
isorig: INVALID (pass)
isfix1: INVALID (ndig == 0) (pass)
IPADDR: 17.233.00.131 -- contain leading zeros
isorig: INVALID (pass)
isfix1: INVALID (leading_zero && (ndig > 1)) (pass)
IPADDR: 17.233.01.131 -- contain leading zeros
isorig: INVALID (pass)
isfix1: INVALID (leading_zero && (ndig > 1)) (pass)
在 POSIX 系统上,use the inet_addr()
function:
NAME
inet_addr
, inet_ntoa
- IPv4 address manipulation
SYNOPSIS
#include <arpa/inet.h>
in_addr_t inet_addr(const char *cp);
char *inet_ntoa(struct in_addr in);
DESCRIPTION
The inet_addr()
function shall convert the string pointed to by cp
, in the standard IPv4 dotted decimal notation, to an integer
value suitable for use as an Internet address.
除非您编写代码是为了练习,否则没有必要重新发明一个已经存在的标准解决方案。
...
RETURN VALUE
Upon successful completion, inet_addr(
) shall return the Internet
address. Otherwise, it shall return ( in_addr_t)(-1)
.
...
我在代码审查网站上发现了这个问题。由于代码没有按预期工作,这个问题不适合代码审查,所以我将在这里提供审查。
幻数
这在之前的回答中提到过:
代码中不清楚47、48、59和156这几个数字是什么意思。
您不能假定使用的字符集是 ASCII,因此使用基于 0
到 9
的范围检查更安全。
if(inputString[i] == '0' || inputString[i] == '.')
{
if(inputString[i+1] < '0' && inputString[i+1] > '9')
{
return false;
}
}
使用ctype.h
提供的isdigit功能可能会更好
if(inputString[i] == '0' || inputString[i] == '.')
{
if(!isdigit(inputString[i+1]))
{
return false;
}
}
复杂性
如果将函数分解为只有一个目的的较小函数,则此代码将更易于阅读、编写和调试。每个内部循环或检查都应该是一个函数。
编程的艺术是将问题分解成越来越小的部分,直到每一部分都非常容易解决的艺术。 Single Responsibility Principle 等编程原则对此进行了描述。
使用 C 库使检查更容易
有 2 个函数可以将字符串转换为整数,这样可以更轻松地检查大于 255 的数字。第一个是 atoi and the second is strtol,这两个函数中的任何一个都会给你一个可以与 255 进行比较的数字。
老实说,我认为我写的代码很垃圾,而且我认为这不是解决问题的最佳方法。我需要一些建议或改进来解决这个问题。我还是编码新手。如果您能提供一些有关如何使用字符串和各种字符串函数的提示,我们将不胜感激。
字符串成为 IP 地址的条件:-
连接到互联网的设备的标识号。以点分四组表示法编写的 IPv4 地址由四个由句点分隔的 8 位整数组成。
换句话说,它是一个由四个数字组成的字符串,每个数字介于 0 和 255 之间(含 0 和 255),带有“.”。每个数字之间的字符。所有数字都应不带前导零。
示例:
- 192.168.0.1 是一个有效的 IPv4 地址
- 255.255.255.255 是一个有效的 IPv4 地址
- 280.100.92.101 不是有效的 IPv4 地址,因为 280 太大而不是 8 位整数(最大的 8 位整数是 255)
- 255.100.81.160.172 不是有效的 IPv4 地址,因为它包含 5 个整数而不是 4
- 1..0.1 不是有效的 IPv4 地址,因为它的格式不正确
- 17.233.00.131 和 17.233.01.131 不是有效的 IPv4 地址,因为它们包含前导零
这是我的代码(我知道这是垃圾而且没有任何意义):-
bool isIPv4Address(char * inputString) {
int count = 0, period = 0;
int k = 0, i = 0;
int digit = 0;
while(inputString[i] != '[=11=]')
{
count = 0;
digit = 0;
i = k;
if(inputString[i] == '0' || inputString[i] == '.')
{
if(inputString[i+1] >47 && inputString[i+1] < 58)
{
return false;
}
}
while(inputString[i] != '.' && inputString[i] != '[=11=]')
{
if(inputString[i] < 48 || inputString[i] > 57)
{
return false;
}
count += (int)inputString[i];
i++;
digit++;
}
if(digit == 3)
{
if(inputString[i - 3] > '2')
{
return false;
}
}
if(inputString[i] == '.')
{
period++;
}
k = i+1;
if(count < 48 || count > 156)
{
return false;
}
if(inputString[i] == '[=11=]')
{
break;
}
}
if(period == 3)
{
return true;
}else
{
return false;
}
}
对于 '0'
之类的东西,您有很多松散的 47、48 等值。最好使用后一种语法。
有许多 if
范围检查。使用一些额外的状态变量可以降低复杂性。
到处使用inputString[i]
很麻烦。最好这样做(例如 int chr = inputString[i];
并改用 chr
——这样更简单易读)。
原程序错误识别于:
192.168.0.1
280.100.92.101
我重构了代码并构建了诊断测试程序。注释为:
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#define ERR(_expr) \
if (err != NULL) \
break; \
if (_expr) { \
err = #_expr; \
break; \
}
const char *bigerr;
int opt_i;
bool
isfix1(const char *inputString)
{
int i = 0;
int ndig = -1;
int ndot = 0;
int val = 0;
int leading_zero = 0;
unsigned char ipv[4];
const char *err = NULL;
while (1) {
int chr = inputString[i++];
int eos = (chr == 0);
// check the last number we got
if ((chr == '.') || eos) {
ERR(ndig == 0);
// we don't allow (because it might be decoded as octal):
// x.00.y.z x.01.y.z
// but this must be ok:
// x.0.y.z
ERR(leading_zero && (ndig > 1));
// we can only have 3 dots in the string
ndot += (! eos);
ERR(ndot > 3);
ndig = 0;
val = 0;
if (eos) {
// we must have _exactly 3 dots in the string
ERR(ndot != 3);
break;
}
continue;
}
// collect digits
if ((chr >= '0') && (chr <= '9')) {
val *= 10;
chr -= '0';
val += chr;
if (ndig < 0)
ndig = 0;
// remember whether substring started with "0"
if (ndig == 0)
leading_zero = (chr == 0);
// we can only have up to 3 digits per byte
++ndig;
ERR(ndig > 3);
// max value for byte
ERR(val > 255);
// just for fun -- the binary IP address
ipv[ndot] = val;
continue;
}
// any invalid character
ERR(chr != 0);
}
if (0)
printf("IPV: %d.%d.%d.%d\n",ipv[0],ipv[1],ipv[2],ipv[3]);
bigerr = err;
return (err == NULL);
}
bool
isorig(const char *inputString)
{
int count = 0,
period = 0;
int k = 0,
i = 0;
int digit = 0;
while (inputString[i] != '[=11=]') {
count = 0;
digit = 0;
i = k;
if (inputString[i] == '0' || inputString[i] == '.') {
if (inputString[i + 1] > 47 && inputString[i + 1] < 58) {
return false;
}
}
while (inputString[i] != '.' && inputString[i] != '[=11=]') {
if (inputString[i] < 48 || inputString[i] > 57) {
return false;
}
count += (int) inputString[i];
i++;
digit++;
}
if (digit == 3) {
if (inputString[i - 3] > '2') {
return false;
}
}
if (inputString[i] == '.') {
period++;
}
k = i + 1;
if (count < 48 || count > 156) {
return false;
}
if (inputString[i] == '[=11=]') {
break;
}
}
if (period == 3) {
return true;
}
else {
return false;
}
}
int
dofnc(bool (*fnc)(const char *),const char *ipaddr,const char *reason)
{
int valid;
bigerr = NULL;
valid = fnc(ipaddr);
printf("%s",valid ? "valid" : "INVALID");
if (bigerr != NULL)
printf(" (%s)",bigerr);
printf(" (%s)\n",(valid != (reason == NULL)) ? "FAIL!" : "pass");
return valid;
}
void
dotest(const char *ipaddr,const char *reason)
{
static int sep = 0;
if (sep)
printf("\n");
sep = 1;
printf("IPADDR: %s",ipaddr);
if (reason != NULL)
printf(" -- %s",reason);
printf("\n");
int valid[2];
printf("isorig: ");
valid[0] = dofnc(isorig,ipaddr,reason);
printf("isfix1: ");
valid[1] = dofnc(isfix1,ipaddr,reason);
do {
if (opt_i)
break;
for (int idx = 0; idx < 2; ++idx) {
if (valid[idx] != (reason == NULL))
exit(1);
}
} while (0);
}
int
main(int argc,char **argv)
{
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
char *cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'i':
opt_i = ! opt_i;
break;
}
}
dotest("192.168.0.1",NULL);
dotest("255.255.255.255",NULL);
dotest("280.100.92.101","280 is too large to be an 8-bit integer");
dotest("255.100.81.160.172","contains 5 integers instead of 4");
dotest("1..0.1","not properly formatted");
dotest("1.0.1.","not properly formatted");
dotest("17.233.00.131","contain leading zeros");
dotest("17.233.01.131","contain leading zeros");
return 0;
}
程序输出如下:
IPADDR: 192.168.0.1
isorig: INVALID (FAIL!)
isfix1: valid (pass)
IPADDR: 255.255.255.255
isorig: valid (pass)
isfix1: valid (pass)
IPADDR: 280.100.92.101 -- 280 is too large to be an 8-bit integer
isorig: valid (FAIL!)
isfix1: INVALID (val > 255) (pass)
IPADDR: 255.100.81.160.172 -- contains 5 integers instead of 4
isorig: INVALID (pass)
isfix1: INVALID (ndot > 3) (pass)
IPADDR: 1..0.1 -- not properly formatted
isorig: INVALID (pass)
isfix1: INVALID (ndig == 0) (pass)
IPADDR: 1.0.1. -- not properly formatted
isorig: INVALID (pass)
isfix1: INVALID (ndig == 0) (pass)
IPADDR: 17.233.00.131 -- contain leading zeros
isorig: INVALID (pass)
isfix1: INVALID (leading_zero && (ndig > 1)) (pass)
IPADDR: 17.233.01.131 -- contain leading zeros
isorig: INVALID (pass)
isfix1: INVALID (leading_zero && (ndig > 1)) (pass)
在 POSIX 系统上,use the inet_addr()
function:
NAME
inet_addr
,inet_ntoa
- IPv4 address manipulationSYNOPSIS
#include <arpa/inet.h> in_addr_t inet_addr(const char *cp); char *inet_ntoa(struct in_addr in);
DESCRIPTION
The
inet_addr()
function shall convert the string pointed to bycp
, in the standard IPv4 dotted decimal notation, to an integer value suitable for use as an Internet address.
除非您编写代码是为了练习,否则没有必要重新发明一个已经存在的标准解决方案。
...
RETURN VALUE
Upon successful completion,
inet_addr(
) shall return the Internet address. Otherwise, it shall return( in_addr_t)(-1)
....
我在代码审查网站上发现了这个问题。由于代码没有按预期工作,这个问题不适合代码审查,所以我将在这里提供审查。
幻数
这在之前的回答中提到过:
代码中不清楚47、48、59和156这几个数字是什么意思。
您不能假定使用的字符集是 ASCII,因此使用基于 0
到 9
的范围检查更安全。
if(inputString[i] == '0' || inputString[i] == '.')
{
if(inputString[i+1] < '0' && inputString[i+1] > '9')
{
return false;
}
}
使用ctype.h
if(inputString[i] == '0' || inputString[i] == '.')
{
if(!isdigit(inputString[i+1]))
{
return false;
}
}
复杂性
如果将函数分解为只有一个目的的较小函数,则此代码将更易于阅读、编写和调试。每个内部循环或检查都应该是一个函数。
编程的艺术是将问题分解成越来越小的部分,直到每一部分都非常容易解决的艺术。 Single Responsibility Principle 等编程原则对此进行了描述。
使用 C 库使检查更容易
有 2 个函数可以将字符串转换为整数,这样可以更轻松地检查大于 255 的数字。第一个是 atoi and the second is strtol,这两个函数中的任何一个都会给你一个可以与 255 进行比较的数字。