java - ¿Cómo declarar una matriz de bytes en Scala?
arrays (7)
En Scala, puedo declarar una matriz de bytes de esta manera
val ipaddr: Array[Byte] = Array(192.toByte, 168.toByte, 1.toByte, 9.toByte)
Esto es demasiado detallado. ¿Existe una forma más sencilla de declarar una matriz de bytes, similar a la de Java?
byte[] ipaddr = {192, 168, 1, 1};
Tenga en cuenta que lo siguiente resulta en un error debido a la .
en la cuerda
InetAddress.getByAddress("192.168.1.1".toByte)
¿Qué hay de Array(192, 168, 1, 1).map(_.toByte)
?
Al agregar métodos a StringContext
se pueden definir fácilmente varios métodos para convertir literales de cadenas en matrices de bytes. Por ejemplo, podemos hacer esto:
val bytes = ip"192.168.1.15"
o esto:
val bytes = hexdump"742d 6761 2e00 6f6e 6574 672e 756e 622e"
Tenga en cuenta que es especialmente útil para trabajar con matrices de bytes en notación hexadecimal, porque escribir el prefijo "0x" delante de cada byte puede volverse muy molesto muy rápidamente, como se puede ver en este ejemplo . Cuando se usa la notación hexadecimal como en Array(0xAB, 0xCD, 0xEF).map(_.toByte)
, no es la llamada a map
que es extraño, es el repetido "0x" -prefix que genera todo el ruido.
Aquí hay un pequeño fragmento de código que muestra cómo se podrían implementar varias formas diferentes para la creación de matrices de bytes al proporcionar una implicit class
que envuelve 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:_*)
}
Tan pronto como esta clase esté en el ámbito implícito, podemos usar todas las siguientes notaciones para definir matrices de bytes:
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")
Tenga en cuenta que los literales de cadena también pueden contener referencias a variables usando la sintaxis $varargVar
dentro de la cadena. Todos los ejemplos generan la misma matriz de bytes [C0,A8,01,0F]
.
En el rendimiento: todo lo anterior se basa en las llamadas a métodos, los literales no se transforman en arrays de bytes en el momento de la compilación.
Creo que lo más corto que puedes hacer es
val ipaddr = Array[Byte](192.toByte, 168.toByte, 1, 9)
Debe convertir 192
y 168
a bytes porque no son literales de bytes válidos, ya que están fuera del rango de bytes firmados ([-128, 127]).
Tenga en cuenta que lo mismo ocurre con Java, lo siguiente da un error de compilación:
byte[] ipaddr = {192, 168, 1, 1};
Tienes que convertir 192 y 168 a bytes:
byte[] ipaddr = {(byte)192, (byte)168, 1, 1};
Para ampliar la respuesta de Chris Martin , si te sientes perezoso y quieres no escribir Array(...).map(_.toByte)
una y otra vez, siempre puedes escribir una función variadic:
def toBytes(xs: Int*) = xs.map(_.toByte).toArray
Ahora puede declarar su matriz de bytes tan concisamente como en Java:
val bytes = toBytes(192, 168, 1, 1) // Array[Byte](-64, -88, 1, 1)
Para su ejemplo específico, simplemente podría usar InetAddress.getByName
lugar:
Array[Byte](1,2,3)
En general, Didier tiene razón, los Byte
son -128 a 127, así que esto funciona:
Array[Byte](192, 168, 1, 1)
pero esto no lo hace
implicit def int2byte(int: Int) = { int.toByte }
Puedes usar implícito
InetAddress.getByName("192.168.1.1")
Y eso convertirá todos los valores de Int en el alcance en lugares donde se requiere un byte.
split
en una String
podría hacer el truco:
val ipaddr: Array[Byte] =
"192.168.1.1".split(''.'').map(_.toInt).map(_.toByte)
Rompiendo esto tenemos
"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)