Как объявить массив байтов в 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)
Ответы
Ответ 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)