leetcode 468. Validate IP Address 验证 IP 地址

好久没刷题了,写了好久,其中 debug 时间浪费生命。记录一下怎么高效率 bug-free 写这种 dirty 的工作。

第一个是要抽象!第二个 dry,第三个是 tdd。我们自顶向下来写。

先写测试

谨记必须要做到每步测试,才不会后期缝合完了再出 bug。

然后写基本框架,这里一个要点是凡是未编写的函数都要写成 return false 的情况(总之不能是需要功能的情况)。

我们第一步是要区分用 ipv4 相关的去判断还是用 v6 相关的。为此应该想到先去判断第一个非数字符号是句号还是冒号。

为了一遍扫描过判断,应该想到使用指针扫描的总体方案,即设定一个全局变量。根据dry思路,我们每个数字块+分隔符作为一个扫描块,编写出来如下代码,基本需要的函数也一目了然了。

这样补全剩下的内容:

由于基本思路是一样的,我们先只做 IPV4 的部分,然后走测试。容易想到的思路是计算一个三位数字,然后判断他是不是小于 256,很容易编写下面的代码,仅当 return 1 的时候我们更新 cur 指针,否则直接跳到返回 Neither 去了。

然后我们通过第一次测试,我们只留下 v4num 参与判断,而注释掉 validate 函数中的其他代码,一次测试多个数字,并且打印 ip 的值证明索引更新正确。这里的要点是更新 ip++ ,所有时候写 while 都要记住迭代变量/索引都要更新。这里迭代变量是 d,索引是 ip,只要根据这个思路,就能避免死循环问题。

通过基本的测试后我们可以回来不全 v4num 的内容,这里的问题是循环数字浪费时间,只需要在3位数的时候停下来即可。第二点是没有考虑 00 的情况,因为根据正确性,之后应该读入一个分隔符,如果没读到分隔符分隔符负责的函数马上就会报错返回,这样基本是最小化函数功能,也就是只判断并更新一个可能正确的块,之后的让之后的去负责,也就不需要判断 \0 。

这里的要点是,对于 while 循环,情况有:一次也没有,走了多次。必须考虑这两种基本情况(这个是递归、 while 迭代的基本考虑点,只要遵循这个准则,就能防止 bug,对于 for 而言,一般是计数循环,可以不用这样考虑)。对于一次也没有的情况,d = 0.

然后我们通过一些样例来测试,基本建议是:".123"  "123" "00" "0" "1234",根据其打印的判断结果和结束点完成测试。而 skipDot 的编写一般是不会犯错的(或者说可以人工看出肯定没有 bug),这里略过。

于是进行完整的 ipv4 部分的测试,基本框架来说 ipv4 都能正确地识别和判断,所以也证明基本框架的代码是正确的(这是由于 ipv6 和 ipv4 的判断流程完全一样,所以以后找 bug 也降低主要函数的优先级)。

然后编写 ipv6 相关的函数,hex 函数可以人眼确认正确性,不需要测试。

这里要点还是很简单,我们只需要遵循循环变量必须更新的准则来写代码就行了,不然写着写着就感觉不知道自己要干什么了,甚至可能写个 len++ 就走人了。

代码:

volatile char* cur;
#define HEX(ch)                                                    \
  (((ch) >= '0' && (ch) <= '9') || ((ch) >= 'a' && (ch) <= 'f') || \
   ((ch) >= 'A' && (ch) <= 'F'))
#define SKIP(ip, ch) ((ip)[0] == (ch) && (cur = (ip) + 1))
#define END(ip) ((ip)[0] == '\0')
#define IPv4 "IPv4"
#define IPv6 "IPv6"
#define Neither "Neither"
int v4num(char* ip) {
  int d = 0;
  if (ip[0] == '0') return 1 && (cur = ip + 1);
  while (*ip >= '0' && *ip <= '9' && d < 255) d = d * 10 + *(ip++) - '0';
  if (d > 255 || !d) return 0;
  cur = ip;
  return 1;
}
int v6num(char* ip) {
  int len = 0;
  while (HEX(ip[len]) && (len++ < 3))
    ;
  cur = ip + len;
  return len ? 1 : 0;
}
char* validIPAddress(char* ip) {
  if (END(ip)) return Nt;
  if (v4num(ip) && SKIP(ip, '.')) {
    for (int i = 0; i < 2; i++)
      if (!v4num(cur) || !SKIP(cur, '.')) return Nt;
    if (v4num(cur) && END(cur)) return v4;
  } else if (v6num(ip) && SKIP(cur, ':')) {
    for (int i = 0; i < 6; i++)
      if (!v6num(cur) || !SKIP(cur, ':')) return Nt;
    if (v6num(cur) && END(cur)) return v6;
  }
  return Nt;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值