小師妹學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的博客

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM