web-dev-qa-db-fra.com

Comment déclarer un tableau d'octets dans Scala?

Dans Scala, je peux déclarer un tableau d'octets de cette façon

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

C'est trop verbeux. Existe-t-il un moyen plus simple de déclarer un tableau d'octets, similaire à Java

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

Notez que les résultats suivants génèrent une erreur due à . dans la chaîne

InetAddress.getByAddress("192.168.1.1".toByte)
35
Hanxue

Je crois que le plus court que vous puissiez faire est

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

Vous devez convertir 192 et 168 en octets car ce ne sont pas des littéraux d'octets valides car ils se trouvent en dehors de la plage d'octets signés ([-128, 127]).

Notez que la même chose vaut pour Java, ce qui suit donne une erreur de compilation:

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

Vous devez convertir 192 et 168 en octets:

byte[] ipaddr = {(byte)192, (byte)168, 1, 1};
39
Zoltán

Que diriez-vous de Array(192, 168, 1, 1).map(_.toByte)?

37
Chris Martin

Pour développer la réponse de Chris Martin, si vous vous sentez paresseux et que vous ne voulez pas taper Array(...).map(_.toByte) encore et encore, vous pouvez toujours écrire une fonction variadique:

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

Vous pouvez maintenant déclarer votre tableau d'octets de manière aussi concise qu'en Java:

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

split sur un String pourrait faire l'affaire:

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

En décomposant cela, nous avons

"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)    
3
Mario Galic

Vous pouvez utiliser implicite

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

Et cela convertira toutes les valeurs Int dans la portée aux endroits où l'octet est requis.

3
Алексей Юр

En ajoutant des méthodes à StringContext on peut facilement définir différentes méthodes pour convertir des littéraux de chaîne en tableaux d'octets. Par exemple, nous pouvons le faire:

val bytes = ip"192.168.1.15"

ou ca:

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

Notez qu'il est particulièrement utile pour travailler avec des tableaux d'octets en notation hexadécimale, car écrire le préfixe "0x" devant chaque octet peut devenir très ennuyeux très rapidement, comme on peut le voir dans cet exemple . Lorsque vous utilisez la notation hexadécimale comme dans Array(0xAB, 0xCD, 0xEF).map(_.toByte), ce n'est pas l'appel à map qui est gênant, c'est le préfixe "0x" répété qui génère tout le bruit.

Voici un petit extrait de code qui montre comment plusieurs façons différentes de créer un tableau d'octets pourraient être implémentées en fournissant un implicit class Qui encapsule un 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:_*)
}

Dès que cette classe est dans la portée implicite, nous pouvons utiliser toutes les notations suivantes pour définir des tableaux d'octets:

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

Notez que les littéraux de chaîne peuvent également contenir des références à des variables utilisant la syntaxe $varargVar À l'intérieur de la chaîne. Tous les exemples génèrent le même tableau d'octets [C0,A8,01,0F].

Sur les performances: tout ce qui précède est construit autour d'appels de méthode, les littéraux ne sont pas transformés en tableaux d'octets au moment de la compilation.

3
Andrey Tyukin

Pour votre exemple spécifique, vous pouvez simplement utiliser InetAddress.getByName au lieu:

InetAddress.getByName("192.168.1.1")

En général, Didier a raison, Bytes vont de -128 à 127, donc ça marche:

Array[Byte](1,2,3)

mais cela ne veut pas:

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