web-dev-qa-db-ja.com

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)
35
Hanxue

あなたができる最短は

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

あなたする必要があります convert 192および168はバイトに変換されます。これは、これらが符号付きバイトの範囲外にあるため有効なバイトリテラルではないためです([-128、127])。

同じことがJavaにも当てはまることに注意してください。以下はコンパイルエラーになります。

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

192および168をバイトにキャストする必要があります。

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

Array(192, 168, 1, 1).map(_.toByte)はどうですか?

37
Chris Martin

Chris Martin's answerを展開するには、怠laで、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)
10
KChaloux

splitStringでトリックを行うことができます:

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

暗黙的に使用できます

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

そして、それは、バイトが必要な場所でスコープ内のすべてのInt値を変換します。

3

StringContext にメソッドを追加することにより、文字列リテラルをバイト配列に変換するためのさまざまなメソッドを簡単に定義できます。たとえば、次のことができます。

_val bytes = ip"192.168.1.15"
_

またはこれ:

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

すべてのバイトの前に「0x」プレフィックスを書き込むと、非常にすぐに迷惑になることがあるため、16進表記のバイト配列を操作するのに特に便利であることに注意してください この例ではArray(0xAB, 0xCD, 0xEF).map(_.toByte)のように16進表記を使用する場合、mapの呼び出しではなく、厄介なのは、すべてのノイズを生成する "0x"プレフィックスの繰り返しです。

以下は、StringContextをラップする_implicit class_を提供することにより、バイト配列作成のいくつかの異なる方法を実装する方法を示す小さなコードスニペットです。

_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]_が生成されます。

パフォーマンスについて:上記のすべてはメソッド呼び出しを中心に構築され、リテラルはコンパイル時にバイト配列に変換されません。

3
Andrey Tyukin

特定の例では、代わりにInetAddress.getByNameを使用できます。

InetAddress.getByName("192.168.1.1")

一般に、ディディエは正しいです、Bytesは-128〜127ですので、これは動作します:

Array[Byte](1,2,3)

しかし、これはしません:

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