欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中文件操作功能小結(jié)

 更新時間:2023年05月22日 16:39:15   作者:HAibiiin  
這篇文章主要為大家整理了一些Java中文件操作功能的相關(guān)資料,文中的示例代碼講解詳細,具有一定的學(xué)習價值,感興趣的小伙伴可以了解一下

文件寫入

為提供相對較高性能的文件讀寫操作,這里果斷選擇了 NIO 對文件的操作,因為業(yè)務(wù)背景需要數(shù)據(jù)的安全落盤。這里主要采用 ByteBuffer 與 FileChannel 的組合,下面是代碼片段示例:

public static void write(String file, String content) throws IOException {
    ByteBuffer writeBuffer = ByteBuffer.allocate(4096);
    int cap = buffer.capacity();
    try (FileChannel fileChannel = FileChannel.open(Path.of(file), StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.READ)) {
        byte[] tmp = content.getBytes(StandardCharsets.UTF_8);
        for (int i = 0; i < tmp.length; i = i + cap) {  
			if (tmp.length < i + cap) {
				buffer.put(tmp, i, tmp.length - i);
			} else {
				buffer.put(tmp, i, cap);
			}
			buffer.flip();
			fileChannel.write(buffer);
			buffer.compact();
        }
        fileChannel.force(false);
    } finally {
        buffer.clear();
    }
}

ByteBuffer

在上面的代碼(基于JDK11)片段中,我們使用 ByteBuffer 作為待讀寫數(shù)據(jù)的載體才能夠配合 FileChannel 一起使用。如果是 JDK8 獲取 FileChannel 可以采用 new RandomAccessFile(new File("xx"), "rw").getChannel() 。在講 ByteBuffer 初始化之前,我們需要先對數(shù)據(jù)單位有一個明確的概念。

KB 不是 kb

我們常看到的 kb 單位對應(yīng) kilobits ,而 KB 單位對應(yīng) kilobyte。Java 中的 1 byte 對應(yīng) 8 bits,所以 1 KB(1024 byte) = 8kb (8196 bits)。包括mb、MB等也是一樣的,為方便記憶,我們只需要記住小寫的 b 表示 bits,而大寫的 B 表示 byte 即可。

接下來初始化采用 allocate() 方法,容量是 4096,因為 ByteBuffer 底層數(shù)據(jù)結(jié)構(gòu)是 byte 數(shù)組,再結(jié)合上面的知識,我們這里創(chuàng)建了 4KB 大小的 Buffer。具體大小需要根據(jù)實際測試進行調(diào)整,普遍的說法是 4KB 的整數(shù)倍會發(fā)揮最大性能優(yōu)勢。

為什么是 4KB 的整數(shù)倍呢?大致就是, 操作系統(tǒng)一次 I/O 操作會以 I/O 塊為單位進行操作,這個 I/O 塊的默認大小是 4KB。但是這個數(shù)值并不嚴謹,它受操作系統(tǒng),磁盤等因素影響,所以需要實際測試后調(diào)整。

初始化

另一種初始化的方式是通過 wrap() 對已存在 byte 數(shù)組進行包裝,應(yīng)用場景會略有不同,兩者區(qū)別如下代碼片段所示:

public static ByteBuffer allocate(int capacity) {
    if (capacity < 0)
        throw createCapacityException(capacity);
    return new HeapByteBuffer(capacity, capacity);
}
HeapByteBuffer(int cap, int lim) {
    super(-1, 0, lim, cap, new byte[cap], 0)
}
public static ByteBuffer wrap(byte[] array, int offset, int length) {
    try {
        return new HeapByteBuffer(array, offset, length);
    } catch (IllegalArgumentException x) {
        throw new IndexOutOfBoundsException();
    }
}
HeapByteBuffer(byte[] buf, int off, int len) {
    super(-1, off, off + len, buf.length, buf, 0)
}

最終調(diào)用的都是 Buffer(int mark, int pos, int lim, int cap) 這個初始化方法,該方法也揭示了 ByteBuffer 的基本屬性:

  • position:表示下一個讀寫操作的起始位置,可通過 position() 方法獲?。?/li>
  • limit:表示下一個讀寫操作的最大位置,可通過 limit() 方法獲取;
  • capacity:表示容量,可通過 capacity() 方法獲取;
  • mark:自定義標記位置;

上述4個屬性的關(guān)系始終滿足:mark <= position <= limit <= capacity。在初始化后ByteBuffer的內(nèi)部結(jié)構(gòu)如下圖所示:

ByteBuffer 操作及屬性變化

通過上圖中結(jié)構(gòu)為 ByteBuffer 初始化的結(jié)構(gòu),寫文件需要向 buffer 中寫入數(shù)據(jù),ByteBuffer 提供了多個 put() 方法,調(diào)用 put() 相關(guān)方法之后,如下圖所示向 buffer 寫入 8 個byte的內(nèi)容后,其內(nèi)部結(jié)構(gòu)主要是 position 指向了后續(xù)插入數(shù)據(jù)的位置:

目前數(shù)據(jù)已經(jīng)寫入了 buffer 中,接下來需要通過 FileChannel 寫入文件,年需要將數(shù)據(jù)從 buffer 中讀出來。在調(diào)用 FileChannel 的 write() 方法之前,需要調(diào)用 buffer 的 flip()  方法,flip() 方法將標識屬性變換為下圖所示,也就是切換為讀取模式,即 position 重置到 0,而 limit 移動到原 position 位置。這樣從 position 讀取到 limit 就是剛剛寫入的數(shù)據(jù):

FileChannel 完成 write 操作后,即 buffer 內(nèi)數(shù)據(jù)讀取完,則 position 的位置會移動到 limit 所在位置。為保證數(shù)據(jù)的完整性,此時需要調(diào)用 buffer 的 compact() 方法將 position 到 limit 間未讀取的數(shù)據(jù)移動到 buffer 的頭部,開啟新的一輪寫入模式,調(diào)用方法后具體的屬性關(guān)系如下圖所示(下圖中例子為數(shù)據(jù)讀 3 個 byte 后調(diào)用compact() 效果,將 position 與 limit 間的數(shù)據(jù)移動到 buffer 的頭部,并將 limit 移動到 capacity 的位置,position 移動到未讀數(shù)據(jù)的末尾):

最后在整個寫文件的結(jié)尾,需要通過 FileChannel 的 force() 方法將數(shù)據(jù)強制刷盤,其實上面的所有操作只是將數(shù)據(jù)寫入了 PageCache 中,具體何時落入磁盤由操作系統(tǒng)調(diào)度,而 force() 方法就是通知操作系統(tǒng)將 PageCache 的內(nèi)容寫入磁盤,這樣才可以確保數(shù)據(jù)真正的持久化到磁盤中。

DirectByteBuffer

還有一種方式是通過 allocateDirect() 方法創(chuàng)建 DirectByteBuffer 采用對外內(nèi)存,如果需要更高的性能,或者需要長期且大數(shù)據(jù)量的 I/O 操作可以采用這種方式。但一定要注意代碼片段確保的 ((DirectBuffer) buffer).cleaner().clear() 對堆外內(nèi)存進行回收(該方法在 JDK11 版本不可直接使用)。

如果不及時清理也會造成內(nèi)存溢出。如下圖所示,左側(cè)為未調(diào)用 clear() 方法前的堆外內(nèi)存使用情況,右側(cè)為調(diào)用后的情況。同時可以配合JVM 參數(shù) -XX:MaxDirectMemorySize 一起使用避免防止內(nèi)存申請過大而導(dǎo)致進程被終止;

文件讀取

這里我們將文件讀取的代碼片段摘錄如下,關(guān)于文件讀取主要是注意中文字符的亂碼問題,因為我們定義的 buffer 是有容量的,一個容量讀滿之后,可能一個中文字符并沒有讀取完整。因為一個中文字符可能需要 2-3 個 byte,有可能存在只讀取 1 個 byte 的情況。

所以需要結(jié)合 CharBuffer 對未讀取完整的中文字符進行緩沖。具體代碼示例如下所示:

public static String read(String file) throws IOException {
    StringBuilder content = new StringBuilder();
    ByteBuffer buffer = ByteBuffer.allocate(4096);
	CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
	CharBuffer cb = CharBuffer.allocate(4096);
	try (FileChannel fileChannel = FileChannel.open(Path.of(file), StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.READ)) {
		while (fileChannel.read(buffer) != -1) {
			buffer.flip();
			//從ByteBuffer讀取數(shù)據(jù)到CharBuffer,最后如果不是完整的字符position的位置不會移動
			//可以認為ByteBuffer中對應(yīng)的字符未被讀取
			decoder.decode(buffer, cb, false);
			cb.flip();
			content.append(cb, cb.position(), cb.limit());
			//將CharBuffer的position強制重制為0
			cb.rewind();
			buffer.compact();
		}
	} finally {
		cb.clear();
		buffer.clear();
	}
	return content.toString();
}

并發(fā)寫入

FileChannel 的 read/write 操作均是線程安全的,但是因為我們不能保證數(shù)據(jù)被一次性寫入,所以數(shù)據(jù)最終落在文件上會是混亂的片段。這里我們采用類似分區(qū)寫的方式,每個線程負責寫入一個分區(qū)文件,最后再執(zhí)行合并操作。

同時這里介紹下 FileLock 這一進程級別的文件鎖,它不能夠?qū)ν惶摂M機內(nèi)多個線程對文件的訪問提供鎖的能力。而且該鎖的具體實現(xiàn)邏輯和操作系統(tǒng)有強相關(guān)。

到此這篇關(guān)于Java中文件操作功能小結(jié)的文章就介紹到這了,更多相關(guān)Java文件操作內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • fastjson 使用方法詳細介紹

    fastjson 使用方法詳細介紹

    Fastjson是一個Java語言編寫的JSON處理器,由阿里巴巴公司開發(fā)。接下來通過本文給大家分享fastjson 使用方法詳細介紹,感興趣的朋友一起看看吧
    2017-11-11
  • Java正則表達式詳解及實用案例(附詳細代碼)

    Java正則表達式詳解及實用案例(附詳細代碼)

    正則表達式是一種強大的字符串處理工具,用于匹配和解析文本,這篇文章主要給大家介紹了關(guān)于Java正則表達式詳解及實用案例的相關(guān)資料,本文通過代碼示例講解了正則表達式的基本語法規(guī)則,包括字符類、預(yù)定義字符類和數(shù)量詞,需要的朋友可以參考下
    2024-11-11
  • mybatis多個plugins的執(zhí)行順序解析

    mybatis多個plugins的執(zhí)行順序解析

    這篇文章主要介紹了mybatis多個plugins的執(zhí)行順序解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Java如何將字符串轉(zhuǎn)為數(shù)字int的三種方式詳析

    Java如何將字符串轉(zhuǎn)為數(shù)字int的三種方式詳析

    這篇文章主要給大家介紹了關(guān)于Java如何將字符串轉(zhuǎn)為數(shù)字int的三種方式,在編程中我們經(jīng)常需要進行各種數(shù)據(jù)類型之間的轉(zhuǎn)換操作,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2023-10-10
  • Java數(shù)據(jù)結(jié)構(gòu)之有向圖的拓撲排序詳解

    Java數(shù)據(jù)結(jié)構(gòu)之有向圖的拓撲排序詳解

    這篇文章主要為大家詳細介紹了Java數(shù)據(jù)結(jié)構(gòu)中有向圖的拓撲排序,文中的示例代碼講解詳細,具有一定的借鑒價值,感興趣的小伙伴可以了解一下
    2022-11-11
  • RocketMQ實現(xiàn)隨緣分BUG小功能示例詳解

    RocketMQ實現(xiàn)隨緣分BUG小功能示例詳解

    這篇文章主要為大家介紹了RocketMQ實現(xiàn)隨緣分BUG小功能示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • java安全編碼指南之:表達式規(guī)則說明

    java安全編碼指南之:表達式規(guī)則說明

    這篇文章主要介紹了java安全編碼指南之:表達式規(guī)則說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • spring boot使用sharding jdbc的配置方式

    spring boot使用sharding jdbc的配置方式

    這篇文章主要介紹了spring boot使用sharding jdbc的配置方式,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12
  • 解讀spring.factories文件配置詳情

    解讀spring.factories文件配置詳情

    這篇文章主要介紹了解讀spring.factories文件配置詳情,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • Springboot jpa @Column命名大小寫問題及解決

    Springboot jpa @Column命名大小寫問題及解決

    這篇文章主要介紹了Springboot jpa @Column命名大小寫問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10

最新評論