如何在Scala中声明一个字节数组?

时间:2014-06-03 08:11:14

标签: java arrays scala

在Scala中,我可以用这种方式声明一个字节数组

val ipaddr: Array[Byte] = Array(192.toByte, 168.toByte, 1.toByte, 9.toByte)

这太冗长了。是否有更简单的方法来声明Byte数组,类似于Java的

byte[] ipaddr = {192, 168, 1, 1};

请注意,由于String

中的.,以下内容会导致错误
InetAddress.getByAddress("192.168.1.1".toByte)

6 个答案:

答案 0 :(得分:39)

我相信你能做的最短的就是

val ipaddr = Array[Byte](192.toByte, 168.toByte, 1, 9)

必须192168转换为字节,因为它们不是有效的字节文字,因为它们超出了有符号字节的范围([-128,127] )。

请注意,Java也是如此,以下是编译错误:

byte[] ipaddr = {192, 168, 1, 1};

您必须将192和168转换为字节:

byte[] ipaddr = {(byte)192, (byte)168, 1, 1};

答案 1 :(得分:37)

Array(192, 168, 1, 1).map(_.toByte)怎么样?

答案 2 :(得分:9)

要扩展 Chris Martin的答案,如果您感到懒惰,并且想要一次又一次地输入Array(...).map(_.toByte) ,你总是可以编写一个可变函数:

def toBytes(xs: Int*) = xs.map(_.toByte).toArray

现在,您可以像在Java中一样简洁地声明您的字节数组:

val bytes = toBytes(192, 168, 1, 1) // Array[Byte](-64, -88, 1, 1)

答案 3 :(得分:3)

您可以使用隐式


    implicit def int2byte(int: Int) = {
      int.toByte
    }

这将在需要byte的位置转换范围内的所有Int值。

答案 4 :(得分:3)

通过向StringContext添加方法,可以轻松定义将字符串文字转换为字节数组的各种方法。例如,我们可以这样做:

val bytes = hexdump"742d 6761 2e00 6f6e 6574 672e 756e 622e"

或者这个:

Array(0xAB, 0xCD, 0xEF).map(_.toByte)

请注意,它对于以十六进制表示法处理字节数组特别有用,因为在每个字节前面写出“0x”前缀会很快变得非常烦人,如in this example所示。当在map中使用十六进制表示法时,对implicit class的调用不是尴尬,而是重复的“0x” - 前缀会产生所有噪声。

这是一个小代码片段,通过提供包裹StringContext的{​​{1}}来说明如何实现字节数组创建的几种不同方式:

implicit class ByteContext(private val sc: StringContext) {

  /** Shortcut to the list of parts passed as separate
    * string pieces.
    */
  private val parts: List[String] = sc.parts.toList

  /** Parses an array of bytes from the input of a `StringContext`.
    *
    * Applies `preprocess` and `separate` and finally `parseByte` 
    * to every string part.
    * Applies `parseByte` to every vararg and interleaves the
    * resulting bytes with the bytes from the string parts.
    *
    * @param preprocess a string preprocessing step applied to every string part
    * @param separate a way to separate a preprocessed string part into substrings for individual bytes
    * @param parseByte function used to parse a byte from a string
    * @param args varargs passed to the `StringContext`
    * @return parsed byte array
    *
    * Uses a mutable `ListBuffer` internally to accumulate
    * the results.
    */      

  private def parseBytes(
    preprocess: String => String, 
    separate: String => Array[String],
    parseByte: String => Byte
  )(args: Any*): Array[Byte] = {

    import scala.collection.mutable.ListBuffer
    val buf = ListBuffer.empty[Byte]

    def partToBytes(part: String): Unit = {
      val preprocessed = preprocess(part)
      if (!preprocessed.isEmpty) {
        separate(preprocessed).foreach(s => buf += parseByte(s))
      }
    }

    // parse all arguments, convert them to bytes,
    // interleave them with the string-parts
    for ((strPart, arg) <- parts.init.zip(args)) {
      partToBytes(strPart)
      val argAsByte = arg match {
        case i: Int => i.toByte
        case s: Short => s.toByte
        case l: Long => l.toByte
        case b: Byte => b
        case c: Char => c.toByte
        case str: String =>  parseByte(str)
        case sthElse => throw new IllegalArgumentException(
          s"Failed to parse byte array, could not convert argument to byte: '$sthElse'"
        )
      }
      buf += argAsByte
    }

    // add bytes from the last part
    partToBytes(parts.last)

    buf.toArray
  }

  /** Parses comma-separated bytes in hexadecimal format (without 0x-prefix),
    * e.g. "7F,80,AB,CD".
    */
  def hexBytes(args: Any*): Array[Byte] = parseBytes(
    s => s.replaceAll("^,", "").replaceAll(",$", ""), // ,AB,CD, -> AB,CD
    _.split(","),
    s => Integer.parseInt(s, 16).toByte
  )(args: _*)

  /** Parses decimal unsigned bytes (0-255) separated by periods,
    * e.g. "127.0.0.1".
    */
  def ip(args: Any*): Array[Byte] = parseBytes(
    s => s.replaceAll("^[.]", "").replaceAll("[.]$", ""), // .1.1. -> 1.1
    _.split("[.]"),
    s => Integer.parseInt(s, 10).toByte
  )(args:_*)

  /** Parses byte arrays from hexadecimal representation with possible 
    * spaces, expects each byte to be represented by exactly two characters,
    * e.g. 
    * "742d 6761 2e00 6f6e 6574 672e 756e 622e".
    */
  def hexdump(args: Any*): Array[Byte] = parseBytes(
    s => s.replaceAll(" ", ""),
    _.grouped(2).toArray,
    s => Integer.parseInt(s, 16).toByte
  )(args: _*)

  /** Parses decimal unsigned bytes (0-255) separated by commas,
    * e.g. "127.0.0.1".
    */
  def decBytes(args: Any*): Array[Byte] = parseBytes(
    s => s.replaceAll("^,", "").replaceAll(",$", ""), // ,127, -> 127
    _.split(","),
    s => Integer.parseInt(s, 10).toByte
  )(args:_*)
}

只要此类处于隐式作用域,我们就可以使用以下所有表示法来定义字节数组:

  def printBytes(bytes: Array[Byte]) = 
    println(bytes.map(b => "%02X".format(b)).mkString("[",",","]"))

  // bunch of variables to be inserted in the strings
  val a: Int = 192
  val b: Long = 168L
  val c: Byte = 1.toByte
  val d: String = "0F"
  val e: String = "15"

  printBytes(ip"192.168.1.15")
  printBytes(ip"192.$b.1.$e")
  printBytes(ip"$a.$b.$c.$e")
  printBytes(hexBytes"C0,A8,01,0F")
  printBytes(hexBytes"C0,$b,$c,0F")
  printBytes(hexBytes"$a,$b,$c,0F")
  printBytes(decBytes"192,$b,1,15")
  printBytes(decBytes"192,168,$c,$e")
  printBytes(decBytes"$a,$b,1,$e")
  printBytes(hexdump"C0A8 010F")
  printBytes(hexdump"$a $b $c $d")
  printBytes(hexdump"C0 $b 01 $d")

请注意,字符串文字还可以包含使用字符串内部$varargVar语法的变量引用。所有示例都生成相同的字节数组[C0,A8,01,0F]

关于性能:以上所有都是围绕方法调用构建的,文字在编译时不会转换为字节数组。

答案 5 :(得分:2)

对于您的具体示例,您只需使用InetAddress.getByName代替:

InetAddress.getByName("192.168.1.1")

一般来说迪迪埃是对的,Byte是-128到127,所以这有效:

Array[Byte](1,2,3)

但这不是:

Array[Byte](192, 168, 1, 1)