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

时间:2022-12-06 21:15:43

In Scala, I can declare a byte array this way

在Scala中,我可以这样声明一个字节数组

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

This is too verbose. Is there a simpler way to declare a Byte array, similar to Java's

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

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

Note that the following results in an error due to the . in the String

注意,下面的结果会导致错误。在字符串中

InetAddress.getByAddress("192.168.1.1".toByte)

7 个解决方案

#1


31  

I believe the shortest you can do is

我相信你能做的最短的

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

You have to convert 192 and 168 to bytes because they are not valid byte literals as they are outside the range of signed bytes ([-128, 127]).

您必须将192和168转换为字节,因为它们不在有符号字节范围内([-128,127])。

Note that the same goes for Java, the following gives a compile error:

注意,Java也是如此,下面给出了一个编译错误:

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

You have to cast 192 and 168 to bytes:

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

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

#2


28  

How about Array(192, 168, 1, 1).map(_.toByte)?

那么数组(192、168、1、1).map(_.toByte)呢?

#3


9  

To expand on Chris Martin's answer, if you're feeling lazy and like you don't want to type out Array(...).map(_.toByte) over and over again, you can always write a variadic function:

为了扩展克里斯·马丁的答案,如果你感到懒惰,不想一遍又一遍地输入数组(…).map(_.toByte),你总是可以写出一个可变的函数:

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

Now you can declare your byte array just about as concisely as in Java:

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

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

#4


2  

For your specific example you could simply use InetAddress.getByName instead:

对于您的特定示例,您可以简单地使用InetAddress。getByName相反:

InetAddress.getByName("192.168.1.1")

In general Didier is right, Bytes are -128 to 127, so this works:

总体上迪迪埃是对的,字节数从-128到127,所以这是可行的:

Array[Byte](1,2,3)

but this doesn't:

但这并不是:

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

#5


2  

You can use implicit

您可以使用隐式


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

And that will convert all Int values in scope in places where byte is required.

这将在需要字节的地方转换所有Int值。

#6


1  

By adding methods to StringContext one can easily define various methods for converting String literals into Byte arrays. For example, we can do this:

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

val bytes = ip"192.168.1.15"

or this:

或:

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

Notice that it is especially useful for working with byte arrays in hexadecimal notation, because writing out the "0x" prefix in front of every byte can become very annoying very quickly, as can be seen in this example. When using hexadecimal notation as in Array(0xAB, 0xCD, 0xEF).map(_.toByte), it's not the call to map that is awkward, it's the repeated "0x"-prefix that generates all the noise.

注意,它对于使用十六进制表示法处理字节数组特别有用,因为在每个字节前面写出“0x”前缀会很快变得非常烦人,如本例所示。当在数组中使用十六进制表示法时(0xAB, 0xCD, 0xEF).map(_.toByte),它不是调用映射的笨拙,它是重复的“0x”-前缀,产生所有的噪声。

Here is a little code snippet that shows how several different ways for byte array creation could be implemented by providing an implicit class that wraps a StringContext:

下面是一个小代码片段,它展示了如何通过提供一个包含StringContext的隐式类来实现几个不同的创建字节数组的方法:

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:_*)
}

As soon as this class is in the implicit scope, we can use all of the following notations to define byte arrays:

一旦这个类在隐式范围内,我们就可以使用以下所有符号来定义字节数组:

  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")

Note that the string literals can also contain references to variables using the $varargVar syntax inside of the string. All examples generate the same byte array [C0,A8,01,0F].

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

On performance: all of the above is build around method calls, the literals are not transformed to byte-arrays at compile time.

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

#7


1  

split on a String could do the trick:

用绳子分开可以做到:

val ipaddr: Array[Byte] = 
  "192.168.1.1".split('.').map(_.toInt).map(_.toByte)    

Breaking this down we have

把这个分解掉

"192.168.1.1"
  .split('.')    // Array[String]("192", "168", "1", "1")
  .map(_.toInt)  // Array[Int](192, 168, 1, 1)
  .map(_.toByte) // Array[Byte](-64, -88, 1, 1)    

#1


31  

I believe the shortest you can do is

我相信你能做的最短的

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

You have to convert 192 and 168 to bytes because they are not valid byte literals as they are outside the range of signed bytes ([-128, 127]).

您必须将192和168转换为字节,因为它们不在有符号字节范围内([-128,127])。

Note that the same goes for Java, the following gives a compile error:

注意,Java也是如此,下面给出了一个编译错误:

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

You have to cast 192 and 168 to bytes:

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

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

#2


28  

How about Array(192, 168, 1, 1).map(_.toByte)?

那么数组(192、168、1、1).map(_.toByte)呢?

#3


9  

To expand on Chris Martin's answer, if you're feeling lazy and like you don't want to type out Array(...).map(_.toByte) over and over again, you can always write a variadic function:

为了扩展克里斯·马丁的答案,如果你感到懒惰,不想一遍又一遍地输入数组(…).map(_.toByte),你总是可以写出一个可变的函数:

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

Now you can declare your byte array just about as concisely as in Java:

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

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

#4


2  

For your specific example you could simply use InetAddress.getByName instead:

对于您的特定示例,您可以简单地使用InetAddress。getByName相反:

InetAddress.getByName("192.168.1.1")

In general Didier is right, Bytes are -128 to 127, so this works:

总体上迪迪埃是对的,字节数从-128到127,所以这是可行的:

Array[Byte](1,2,3)

but this doesn't:

但这并不是:

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

#5


2  

You can use implicit

您可以使用隐式


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

And that will convert all Int values in scope in places where byte is required.

这将在需要字节的地方转换所有Int值。

#6


1  

By adding methods to StringContext one can easily define various methods for converting String literals into Byte arrays. For example, we can do this:

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

val bytes = ip"192.168.1.15"

or this:

或:

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

Notice that it is especially useful for working with byte arrays in hexadecimal notation, because writing out the "0x" prefix in front of every byte can become very annoying very quickly, as can be seen in this example. When using hexadecimal notation as in Array(0xAB, 0xCD, 0xEF).map(_.toByte), it's not the call to map that is awkward, it's the repeated "0x"-prefix that generates all the noise.

注意,它对于使用十六进制表示法处理字节数组特别有用,因为在每个字节前面写出“0x”前缀会很快变得非常烦人,如本例所示。当在数组中使用十六进制表示法时(0xAB, 0xCD, 0xEF).map(_.toByte),它不是调用映射的笨拙,它是重复的“0x”-前缀,产生所有的噪声。

Here is a little code snippet that shows how several different ways for byte array creation could be implemented by providing an implicit class that wraps a StringContext:

下面是一个小代码片段,它展示了如何通过提供一个包含StringContext的隐式类来实现几个不同的创建字节数组的方法:

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:_*)
}

As soon as this class is in the implicit scope, we can use all of the following notations to define byte arrays:

一旦这个类在隐式范围内,我们就可以使用以下所有符号来定义字节数组:

  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")

Note that the string literals can also contain references to variables using the $varargVar syntax inside of the string. All examples generate the same byte array [C0,A8,01,0F].

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

On performance: all of the above is build around method calls, the literals are not transformed to byte-arrays at compile time.

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

#7


1  

split on a String could do the trick:

用绳子分开可以做到:

val ipaddr: Array[Byte] = 
  "192.168.1.1".split('.').map(_.toInt).map(_.toByte)    

Breaking this down we have

把这个分解掉

"192.168.1.1"
  .split('.')    // Array[String]("192", "168", "1", "1")
  .map(_.toInt)  // Array[Int](192, 168, 1, 1)
  .map(_.toByte) // Array[Byte](-64, -88, 1, 1)