小師妹學JavaIO之:文件寫入那些事

簡介

小師妹又對F師兄提了一大堆奇奇怪怪的需求,要格式化輸出,要特定的編碼輸出,要自己定位輸出,什麼?還要閱後即焚?大家看F師兄怎麼一一接招吧。

字符輸出和字節輸出

小師妹:F師兄,上次你的IO講到了一半,文件讀取是基本上講完了,但是文件的寫入還沒有講,什麼時候給小師妹我再科普科普?

小師妹:F師兄,你知道我這個人一直以來都是勤奮好學的典範,是老師們眼中的好學生,同學們心中的好榜樣,父母身邊乖巧的好孩子。在我永攀科學高峯的時候,居然發現還有一半的知識沒有獲取,真是讓我扼腕嘆息,F師兄,快快把知識傳給我吧。

小師妹你的請求,師兄我自當盡力辦到,但是我怎麼記得上次講IO文件讀取已經過了好幾天了,怎麼今天你纔來找我。

小師妹紅着臉:F師兄,這不是使用的時候遇到了點問題,纔想找你把知識再複習一遍。

更多精彩內容且看:

那先把輸出類的結構再過一遍:

上面就是輸出的兩大系統了:Writer和OutputStream。

Writer主要針對於字符,而Stream主要針對Bytes。

Writer中最最常用的就是FileWriter和BufferedWriter,我們看下一個最基本寫入的例子:

public void useBufferedWriter() throws IOException {
        String content = "www.flydean.com";
        File file = new File("src/main/resources/www.flydean.com");

        FileWriter fw = new FileWriter(file);
        try(BufferedWriter bw = new BufferedWriter(fw)){
            bw.write(content);
        }
    }

BufferedWriter是對FileWriter的封裝,它提供了一定的buffer機制,可以提高寫入的效率。

其實BufferedWriter提供了三種寫入的方式:

public void write(int c)
public void write(char cbuf[], int off, int len)
public void write(String s, int off, int len)

第一個方法傳入一個int,第二個方法傳入字符數組和開始讀取的位置和長度,第三個方法傳入字符串和開始讀取的位置和長度。是不是很簡單,完全可以理解?

小師妹:不對呀,F師兄,後面兩個方法的參數,不管是char和String都是字符我可以理解,第一個方法傳入int是什麼鬼?

小師妹,之前跟你講的道理是不是都忘記的差不多了,int的底層存儲是bytes,char和String的底層存儲也是bytes,我們把int和char做個強制轉換就行了。我們看下是怎麼轉換的:

public void write(int c) throws IOException {
        synchronized (lock) {
            ensureOpen();
            if (nextChar >= nChars)
                flushBuffer();
            cb[nextChar++] = (char) c;
        }
    }

還記得int需要佔用多少個字節嗎?4個,char需要佔用2個字節。這樣強制從int轉換到char會有精度丟失的問題,只會保留低位的2個字節的數據,高位的兩個字節的數據會被丟棄,這個需要在使用中注意。

看完Writer,我們再來看看Stream:

public void useFileOutputStream() throws IOException {
        String str = "www.flydean.com";
        try(FileOutputStream outputStream = new FileOutputStream("src/main/resources/www.flydean.com");
            BufferedOutputStream bufferedOutputStream= new BufferedOutputStream(outputStream)){
            byte[] strToBytes = str.getBytes();
            bufferedOutputStream.write(strToBytes);
        }
    }

跟Writer一樣,BufferedOutputStream也是對FileOutputStream的封裝,我們看下BufferedOutputStream中提供的write方法:

public synchronized void write(int b)
public synchronized void write(byte b[], int off, int len)

比較一下和Writer的區別,BufferedOutputStream的方法是synchronized的,並且BufferedOutputStream是直接對byte進行操作的。

第一個write方法傳入int參數也是需要進行截取的,不過這次是從int轉換成byte。

格式化輸出

小師妹:F師兄,我們經常用的System.out.println可以直接向標準輸出中輸出格式化過後的字符串,文件的寫入是不是也有類似的功能呢?

肯定有,PrintWriter就是做格式化輸出用的:

public void usePrintWriter() throws IOException {
        FileWriter fileWriter = new FileWriter("src/main/resources/www.flydean.com");
        try(PrintWriter printWriter = new PrintWriter(fileWriter)){
            printWriter.print("www.flydean.com");
            printWriter.printf("程序那些事 %s ", "非常棒");
        }
    }

輸出其他對象

小師妹:F師兄,我們看到可以輸出String,char還有Byte,那可不可以輸出Integer,Long等基礎類型呢?

可以的,使用DataOutputStream就可以做到:

public void useDataOutPutStream()
            throws IOException {
        String value = "www.flydean.com";
        try(FileOutputStream fos = new FileOutputStream("src/main/resources/www.flydean.com")){
            DataOutputStream outStream = new DataOutputStream(new BufferedOutputStream(fos));
            outStream.writeUTF(value);
        }
    }

DataOutputStream提供了writeLong,writeDouble,writeFloat等等方法,還可以writeUTF!

在特定的位置寫入

小師妹:F師兄,有時候我們不需要每次都從頭開始寫入到文件,能不能自定義在什麼位置寫入呢?

使用RandomAccessFile就可以了:

public void useRandomAccess() throws IOException {
        try(RandomAccessFile writer = new RandomAccessFile("src/main/resources/www.flydean.com", "rw")){
            writer.seek(100);
            writer.writeInt(50);
        }
    }

RandomAccessFile可以通過seek來定位,然後通過write方法從指定的位置寫入。

給文件加鎖

小師妹:F師兄,最後還有一個問題,怎麼保證我在進行文件寫的時候別人不會覆蓋我寫的內容,不會產生衝突呢?

FileChannel可以調用tryLock方法來獲得一個FileLock鎖,通過這個鎖,我們可以控制文件的訪問。

public void useFileLock()
            throws IOException {
        try(RandomAccessFile stream = new RandomAccessFile("src/main/resources/www.flydean.com", "rw");
        FileChannel channel = stream.getChannel()){
            FileLock lock = null;
            try {
                lock = channel.tryLock();
            } catch (final OverlappingFileLockException e) {
                stream.close();
                channel.close();
            }
            stream.writeChars("www.flydean.com");
            lock.release();
        }
    }

總結

今天給小師妹講了好多種文件的寫的方法,夠她學習一陣子了。

本文的例子https://github.com/ddean2009/learn-java-io-nio

本文作者:flydean程序那些事

本文鏈接:http://www.flydean.com/io-file-writer/

本文來源:flydean的博客

歡迎關注我的公衆號:程序那些事,更多精彩等着您!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章