首页 > 解决方案 > 在 Scala 中验证 IP 地址

问题描述

我想验证 scala 中的 IP 地址。我已经实现了该方法,但我看到了一些错误。

任何人都可以调查它并建议他们是否有更好的解决方案。我不想在功能上实现。

 def validIPAddress(IP: String): String = {

       var isValidIP = true
       if(IP.contains(":")) {  
            var numbers= IP.split(":")   //create an array
            if (numbers.length!=8) return "Neither"        
            for (n <- numbers) {
                if(n.length = 0 or n.length > 4) return "Neither"    
                else {
                    for (m <- n) { 
                        if(!m in "0123456789abcdefABCDEF") return "Neither"
                    }
                }
            }
            return "IPv6"
       }
       else if(IP.contains(".") {     //192.168.1.1
              nums = IP.split(".")
              if(nums.length!=4) isValidIP = false  //Array.length
              else {
                for (a <- nums) {
                  println(a)
                  try {
                    var intA = a.toInt
                    if(intA <=0 || IntA > 255) {
                      isValid = false
                    } 

                  } catch (NumberFormatException e)  {
                    case e: Exception => None
                  }
                }

                if(isValid == true) {
                  println("Valid IP")
                } else {
                  println("Invalid IP") 
                }


              }
       } return "IPv4"

       else {
         return "neither"
       }

       }

标签: scala

解决方案


您可能不打算实现功能,但在您的示例中根本不需要命令式代码,并且returns 和vars 在阅读代码的意图时会引起一些严重的问题。

我会将代码重写为这样的

sealed trait IP extends Product with Serializable
object IP {
  final case class V4(a: Int, b: Int, c: Int, d: Int) extends IP
  final case class V6(a: Int, b: Int, c: Int, d: Int, e: Int, f: Int, g: Int, h: Int) extends IP
}

val ipV4 = """(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})""".r
val ipV6 = """([0-9a-fA-F]+):""".r

def validIPAddress(ip: String): Either[String, IP] = {
  def parseV4(s: String) = {
    val i = Integer.parseInt(s)
    if (0 <= i && i <= 255) Right(i) else Left(s"$i is not a valid IPv4 number")
  }
  def parseV6(s: String) = Integer.parseInt(s, 16)

  ip match {
    case ipV4(a, b, c, d) =>
      for {
        a1 <- parseV4(a)
        b1 <- parseV4(b)
        c1 <- parseV4(c)
        d1 <- parseV4(d)
      } yield IP.V4(a1, b1, c1, d1)
    case ipV6(a, b, c, d, e, f, g, h) =>
      // technically speaking this isn't exhausting all possible IPv6 addresses...
      // as this regexp would ignore e.g. ::1 or ::
      Right(IP.V6(parseV6(a), parseV6(b), parseV6(c), parseV6(d), parseV6(e), parseV6(f), parseV6(g), parseV6(h)))
    case _ =>
      Left(s"$ip is neither V4 nor V6 format")
  }
}

以便更容易地跟踪和调试错误,尽管当我检查此实现并不真正符合 IPv6 所做的事情时,因为需要对正则表达式的使用进行一些调整。事实上,如果我没有时间写下 15-20 个测试用例,我宁愿完全避免推出自己的解决方案。

因此,除非有理由实现您自己的验证器,否则我会将该任务委托给一些已经测试过处理极端情况的库。

但是,如果您必须执行以下操作:

  • 不要使用return- 在 Scala 中,它的作用与您认为的不同
  • 除非您正在优化,否则不要使用vars - 您使用它们的方式与优化无关(isValid从未使用过,所以为什么要覆盖它?并且其他变量永远不会被修改)
  • 不要对所有事情都使用字符串,因为这本身就是自找麻烦(例如"neither""Neither"调用者应该.equalsIgnoreCase在你的代码上使用来检查结果吗?)
  • 从基于规范编写的测试开始。当我翻译您的代码时,它无法使用有效的 IPv6 地址。如果我跳过 Iv6 是固定大小的要求,它会更短:
sealed trait IP extends Product with Serializable
object IP {
  final case class V4(a: Int, b: Int, c: Int, d: Int) extends IP
  final case class V6(values: List[Int]) extends IP
}

val ipV4 = """(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})""".r
val ipV6 = """([0-9a-fA-F]*)((:[0-9a-fA-F]*){2,})""".r

def validIPAddress(ip: String): Either[String, IP] = {
  def parseV4(s: String) = {
    val i = Integer.parseInt(s)
    if (0 <= i && i <= 255) Right(i) else Left(s"$i is not a valid IPv4 number")
  }
  def parseV6(s: String) = if (s.isEmpty) 0 else Integer.parseInt(s, 16)

  ip match {
    case ipV4(a, b, c, d) =>
      for {
        a1 <- parseV4(a)
        b1 <- parseV4(b)
        c1 <- parseV4(c)
        d1 <- parseV4(d)
      } yield IP.V4(a1, b1, c1, d1)
    case ipV6(head, tail, _) =>
      val segments = head +: tail.substring(1).split(':')
      Right(IP.V6(segments.map(parseV6).toList))
    case _ =>
      Left(s"$ip is neither V4 nor V6 format")
  }
}

它处理了更多正确的案例,但还远未准备好。因此,如果可以的话 - 避免自己动手并使用图书馆。


推荐阅读