2012-02-17 199 views
2

如何在scala中讀取二進制文件塊。從二進制文件讀取Scala

這正是我試圖做

val fileInput = new FileInputStream("tokens") 
    val dis = new DataInputStream(fileInput) 
    var value = dis.readInt() 
    var i=0; 
println(value) 

其印刷是一個龐大的數字值。而它應該返回1作爲第一個輸出

+0

一定要具有文件在正確的endianess。 – ziggystar 2012-02-17 21:00:16

+0

究竟是哪一個數字呢?它碰巧是16777216?如果是這樣,你有一個endian問題。 – 2012-02-17 21:02:27

+0

是的它的16777216 ..它搞亂了endian。它應該是1.我如何糾正它? – Gaurav 2012-02-17 21:03:16

回答

10

因爲你看到16777216你期望有一個1,這聽起來像問題是該文件的endianness是不同於JVM所期望的。 (即,Java always expects big endian/network byte order和您的文件包含小端的數字。)

這是一個已經確立的色域解決方案的問題。

對於如何解決這個問題,最簡單的答案就是在讀取它們時簡單地交換字節。你可以做到這一點的更換你的線,看起來像

var value = dis.readInt() 

var value = java.lang.Integer.reverseBytes(dis.readInt()) 

如果你想使這一點更簡潔,你可以使用的隱含添加readXLE(無論是方法)方法到DataInput,或者您可以重寫DataInputStream以使其具有readXLE()方法。不幸的是,Java作者決定應該是最終的,所以我們不能覆蓋那些爲小端文件提供透明閱讀器的方法。

object LittleEndianImplicits { 
    implicit def dataInputToLittleEndianWrapper(d: DataInput) = new DataInputLittleEndianWrapper(d) 

    class DataInputLittleEndianWrapper(d: DataInput) { 
    def readLongLE(): Long = java.lang.Long.reverseBytes(d.readLong()) 
    def readIntLE(): Int = java.lang.Integer.reverseBytes(d.readInt()) 
    def readCharLE(): Char = java.lang.Character.reverseBytes(d.readChar()) 
    def readShortLE(): Short = java.lang.Short.reverseBytes(d.readShort()) 
    } 
} 

class LittleEndianDataInputStream(i: InputStream) extends DataInputStream(i) { 
    def readLongLE(): Long = java.lang.Long.reverseBytes(super.readLong()) 
    def readIntLE(): Int = java.lang.Integer.reverseBytes(super.readInt()) 
    def readCharLE(): Char = java.lang.Character.reverseBytes(super.readChar()) 
    def readShortLE(): Short = java.lang.Short.reverseBytes(super.readShort()) 
} 

object M { 
    def main(a: Array[String]) { 
    println("// Regular DIS") 
    val d = new DataInputStream(new java.io.FileInputStream("endian.bin")) 
    println("Int 1: " + d.readInt()) 
    println("Int 2: " + d.readInt()) 

    println("// Little Endian DIS") 
    val e = new LittleEndianDataInputStream(new java.io.FileInputStream("endian.bin")) 
    println("Int 1: " + e.readIntLE()) 
    println("Int 2: " + e.readIntLE()) 

    import LittleEndianImplicits._ 
    println("// Regular DIS with readIntLE implicit") 
    val f = new DataInputStream(new java.io.FileInputStream("endian.bin")) 
    println("Int 1: " + f.readIntLE()) 
    println("Int 2: " + f.readIntLE()) 
    } 
} 

上面提到的「endian.bin」文件包含一個大端1海灣跟着小尾數1.運行上述M.main()打印:

// Regular DIS 
Int 1: 1 
Int 2: 16777216 
// LE DIS 
Int 1: 16777216 
Int 2: 1 
// Regular DIS with readIntLE implicit 
Int 1: 16777216 
Int 2: 1 
+0

@Gaurav,它可能很明顯,但你可以考慮擴展DataInputStream。 – 2012-02-18 02:23:02

+0

@EdStaub,你會認爲Java擴展DataInputStream相當不方便,因爲該類將readInt()等標記爲final? – 2012-02-19 15:35:29

+0

哎呀 - 應該檢查一下,我已經被燒得足以結束了。代表,那麼,如果它是值得的。在這個例子中,很少使用DataInputStream方法,它可能是。 – 2012-02-19 16:21:53