Подтвердить что ты не робот

Как объявить массив байтов в Scala?

В Scala я могу объявить массив байтов таким образом

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

Это слишком многословно. Есть ли более простой способ объявить массив байтов, аналогичный Java

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

Обратите внимание, что следующие результаты в ошибке из-за . в строке

InetAddress.getByAddress("192.168.1.1".toByte)
4b9b3361

Ответ 1

Я считаю, что самое короткое, что вы можете сделать, это

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

Вы должны преобразовывать 192 и 168 в байты, потому что они не являются допустимыми байтовыми литералами, поскольку они находятся за пределами диапазона подписанных байтов ([-128, 127]).

Обратите внимание, что то же самое относится к Java, следующее дает ошибку компиляции:

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

Вам нужно указать 192 и 168 байтов:

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

Ответ 2

Как насчет Array(192, 168, 1, 1).map(_.toByte)?

Ответ 3

Чтобы расширить ответ Криса Мартина, если вы чувствуете себя ленивым и, как будто не хотите вводить 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)

Ответ 4

Для вашего конкретного примера вы можете просто использовать InetAddress.getByName вместо этого:

InetAddress.getByName("192.168.1.1")

В общем, Дидье прав, Byte - от -128 до 127, поэтому это работает:

Array[Byte](1,2,3)

но это не так:

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

Ответ 5

Вы можете использовать неявные

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

И это преобразует все значения Int в область в местах, где требуется байт.

Ответ 6

Добавляя методы в StringContext можно легко определить различные методы преобразования строковых литералов в массивы байтов. Например, мы можем это сделать:

val bytes = ip"192.168.1.15"

или это:

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

Обратите внимание, что это особенно полезно для работы с байтовыми массивами в шестнадцатеричной нотации, поскольку запись префикса "0x" перед каждым байтом может очень сильно раздражать, как видно из этого примера. При использовании шестнадцатеричной нотации, как в Array(0xAB, 0xCD, 0xEF).map(_.toByte), это не вызов map который неудобен, это повторяющийся "0x" -prefix, который генерирует весь шум.

Вот небольшой фрагмент кода, который показывает, как можно реализовать несколько различных способов создания массива байтов, предоставляя implicit class который обертывает 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:_*)
}

Как только этот класс находится в неявной области, мы можем использовать все следующие обозначения для определения массивов байтов:

  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].

По производительности: все вышеописанное построено вокруг вызовов методов, литералы не преобразуются в байтовые массивы во время компиляции.

Ответ 7

split на String может сделать трюк:

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

Разрушая это, мы имеем

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