我有一個用UTF8(語言特定字符)編碼的文本文件。 我需要使用的RandomAccessFile尋求特定位置和讀取。如何使用RandomAccessFile讀取UTF8編碼的文件?
我想讀行由行。
String str = myreader.readLine(); //returns wrong text, not decoded
String str myreader.readUTF(); //An exception occurred: java.io.EOFException
我有一個用UTF8(語言特定字符)編碼的文本文件。 我需要使用的RandomAccessFile尋求特定位置和讀取。如何使用RandomAccessFile讀取UTF8編碼的文件?
我想讀行由行。
String str = myreader.readLine(); //returns wrong text, not decoded
String str myreader.readUTF(); //An exception occurred: java.io.EOFException
的API文檔說以下爲readUTF8
讀取來自該文件中的字符串。該字符串已使用修改後的UTF-8格式編碼爲 。
讀取前兩個字節,從當前文件指針 開始讀取,就像readUnsignedShort一樣。此值給出編碼字符串中的以下 字節的編號,而不是生成的 字符串的長度。接下來的字節將被解釋爲編碼爲 字符的字節,並且被轉換爲 字符。
該方法會阻塞,直到讀取所有字節,檢測到流 的末端或引發異常。
是您的字符串,這樣格式化?
這似乎說明你EOF exceptuon。
你的文件是一個文本文件,因此您的實際問題是解碼。
我知道最簡單的回答是:
try(BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("jedis.txt"),"UTF-8"))){
String line = null;
while((line = reader.readLine()) != null){
if(line.equals("Obi-wan")){
System.out.println("Yay, I found " + line +"!");
}
}
}catch(IOException e){
e.printStackTrace();
}
或者你可以在當前系統編碼與系統屬性file.encoding
設置爲UTF-8。
java -Dfile.encoding=UTF-8 com.jediacademy.Runner arg1 arg2 ...
您還可以將其設置爲在與System.setProperty(...)
運行時的系統性能,如果你只需要爲這個特定的文件,但在這樣的情況下,我想我會喜歡OutputStreamWriter
。
通過設置系統屬性,您可以使用FileReader
,並期望它將使用UTF-8作爲文件的默認編碼。在這種情況下,您讀取和寫入的所有文件。
如果您打算檢測文件中的解碼錯誤,您將不得不使用InputStreamReader
方法並使用接收解碼器的構造函數。
有點像
CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
decoder.onMalformedInput(CodingErrorAction.REPORT);
decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
BufeferedReader out = new BufferedReader(new InpuStreamReader(new FileInputStream("jedis.txt),decoder));
您可以使用RandomAccessFile
行動IGNORE | REPLACE | REPORT
編輯
如果你堅持之間選擇,你需要知道該行的確切的偏移,你正打算閱讀。不僅如此,爲了用readUTF()
方法來閱讀,你應該用writeUTF()
方法寫下文件。由於此方法(如上面所述的JavaDocs所示)需要特定的格式,其中前2個無符號字節表示UTF-8字符串的字節長度。
因此,如果你這樣做:
try(RandomAccessFile raf = new RandomAccessFile("jedis.bin", "rw")){
raf.writeUTF("Luke\n"); //2 bytes for length + 5 bytes
raf.writeUTF("Obiwan\n"); //2 bytes for length + 7 bytes
raf.writeUTF("Yoda\n"); //2 bytes for lenght + 5 bytes
}catch(IOException e){
e.printStackTrace();
}
你不應該使用方法readUTF()
從該文件讀回,只要任何問題,因爲你能確定給定線的偏移量要回過頭再讀。
如果你打開文件jedis.bin
你會注意到它是一個二進制文件,而不是一個文本文件。
現在,我知道"Luke\n"
是UTF-8中的5個字節,而"Obiwan\n"
是UTF-8中的7個字節。並且writeUTF()
方法將在每個這些字符串的前面插入2個字節。因此,在"Yoda\n"
之前有(5 + 2)+(7 + 2)= 16字節。
所以,我可以做這樣的事情,以達到最後一行:
try (RandomAccessFile raf = new RandomAccessFile("jedis.bin", "r")) {
raf.seek(16);
String val = raf.readUTF();
System.out.println(val); //prints Yoda
} catch (IOException e) {
e.printStackTrace();
}
但是,如果你有一個Writer
類寫的文件,因爲作家不按該方法的格式規則,這將不起作用writeUFT()
。
在這種情況下,最好的做法是,您的二進制文件將被格式化爲所有字符串佔用相同的空間量(字節數,而不是字符數),因爲字節在UTF-8中是可變的,這取決於字符串中的字符),如果不是所有的空間都需要它,你可以使用它:
這樣你就可以很容易地計算給定行的偏移量,因爲它們都佔據相同的空間量。
你不可能這樣做。 seek
函數將定位您的一些字節數。無法保證您與UTF-8字符邊界對齊。
,如果我使用建議的參數java -Dfile.encoding = UTF-8? – kenny 2012-04-01 15:16:02
@kenny UTF-8編碼對可變字節數的字符進行編碼,因此跳到文件內的字節偏移量可能會失敗(因爲@tchrist提到過),當您處於字符邊界的開始時,您可能不會到達那裏。如果您知道需要的字符偏移量,可以使用'Reader.skip(long n)'來跳過字符數。這應該是編碼意識。只要確保在'InputStreamReader'上設置你的字符集。 – 2012-04-01 15:44:37
查找UTF-8中的下一個字符很容易。只需跳過[0x80-0xBF]中的所有字節,不在該範圍內的第一個字符將成爲字符的開頭。 (這是Ken Thompson在UTF-8中添加的自同步屬性)。 – ninjalj 2012-04-02 18:52:51
我覺得RandomAccessFile
的API具有挑戰性。
如果您的文本實際上僅限於UTF-8值0-127(UTF-8的最低7位),那麼使用readLine()
是安全的,但仔細閱讀這些Javadoc:這是一種奇怪的方法。引用:
該方法從文件中連續讀取字節,從當前文件指針開始,直到達到行結束符或文件末尾。通過取字符的低8位的字節值並將字符的高8位設置爲零來將每個字節轉換爲字符。因此,此方法不支持完整的Unicode字符集。
要讀取UTF-8安全,我建議你閱讀(部分或全部)的原始字節與length()
和read(byte[])
的組合。然後使用此構造函數將您的UTF-8字節轉換爲Java String
:new String(byte[], "UTF-8")
。
要安全地編寫UTF-8,請首先將您的Java String
轉換爲帶有someText.getBytes("UTF-8")
的正確字節。最後,使用write(byte[])
寫入字節。
您可以將字符串轉換,通過的readLine讀UTF-8,使用下面的代碼:(UTF8編碼)
Привет из Украины
public static void main(String[] args) throws IOException {
RandomAccessFile raf = new RandomAccessFile(new File("MyFile.txt"), "r");
String line = raf.readLine();
String utf8 = new String(line.getBytes("ISO-8859-1"), "UTF-8");
System.out.println("Line: " + line);
System.out.println("UTF8: " + utf8);
}
Line: ÐÑÐ¸Ð²ÐµÑ Ð¸Ð· УкÑаинÑ
UTF8: Привет из Украины
我意識到這是一個老問題,但它似乎仍然有一些興趣,並且沒有被接受的答案。
你所描述的實質上是一個數據結構問題。這裏討論的UTF8是一個紅色的鯡魚 - 你會面對同樣的問題,使用固定長度的編碼,如ASCII,因爲你有可變長度的線。你需要的是某種索引。
如果你絕對不能改變文件本身(「字符串文件」) - 似乎是這種情況 - 你總是可以構造一個外部索引。第一次(和第一次只有)字符串文件被訪問,你一直讀取它(順序),記錄每行開始的字節位置,並通過記錄文件結束位置(使生活更簡單)。這可以通過下面的代碼來實現:
myList.add(0); // assuming first string starts at beginning of file
while ((line = myRandomAccessFile.readLine()) != null) {
myList.add(myRandomAccessFile.getFilePointer());
}
,然後寫這些整數到一個單獨的文件(「索引文件」),你會讀回以後每一次啓動程序,並打算訪問字符串文件。要訪問第012個字符串,請從索引文件中選取n
th和n+1
th索引(稱這些索引文件爲A
和B
)。然後,您嘗試將A
定位在字符串文件中,並讀取B-A
字節,然後您可以從UTF8中解碼。例如,爲了得到線i
:
myRandomAccessFile.seek(myList.get(i));
byte[] bytes = new byte[myList.get(i+1) - myList.get(i)];
myRandomAccessFile.readFully(bytes);
String result = new String(bytes, "UTF-8");
在許多情況下,但是,這將是更好地使用數據庫,如SQLite,它創建和維護索引你。這樣,您可以添加和修改額外的「行」,而無需重新創建整個索引。有關Java實現,請參閱https://www.sqlite.org/cvstrac/wiki?p=SqliteWrappers。
讀通過的readLine文件()爲我工作:
RandomAccessFile raf = new RandomAccessFile(...);
String line;
while ((line = raf.readLine()) != null) {
String utf = new String(line.getBytes("ISO-8859-1"));
...
}
// my file content has been created with:
raf.write(myStringContent.getBytes());
我創建使用的BufferedWriter(新OutputStreamWriter(新FileOutputStream中(..),編碼),其中編碼是UTF8 – kenny 2012-04-01 14:26:11
然後OU不能使用這個文本文件RandomAccessFile來讀取它,你必須使用BufferedReader或FileReader這樣的閱讀器類,並從頭開始閱讀,直到你到達 – 2012-04-01 14:42:49
這一行,這是不高效的,我使用seek來執行分頁。我每次重讀整個文件 – kenny 2012-04-01 15:44:06