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)
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};
Que diriez-vous de Array(192, 168, 1, 1).map(_.toByte)
?
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)
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)
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.
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.
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, Byte
s vont de -128 à 127, donc ça marche:
Array[Byte](1,2,3)
mais cela ne veut pas:
Array[Byte](192, 168, 1, 1)