Java Advanced-File and IO

Posted by samvelyano on Mon, 29 Jul 2019 13:28:45 +0200

File

Concept

java.io.File is used to represent files (directories) and files and directories on hard disks can be manipulated through File classes in programs. File class can only be used to describe the attribute information (name, size, etc.) of the file (directory), and can not access the content of the file (that is, it does not have the function of reading information from the file and writing information to the file).

File path

File paths use relative paths as much as possible, and "." denotes the current path (the root directory of the project where the current class is located). File.separator constant is used to replace the file path separator as much as possible. It will return different file path separators according to different operating systems (windows returns and Linux returns /).

Source code

public class File implements Serializable, Comparable<File>{
    //......
}

Construction method

File(String pathname) Create a new File instance by converting a given path name string to an abstract path name
File(URI uri) Create a new File instance by converting a given URI to an abstract path name
File(String parent, String child) Create a new File instance based on parent path name string and child path name string
File(File parent, String child) Create a new File instance based on parent Abstract path name and child path name string.

Use

/**
 * Creating objects from a given Abstract path simply creates file objects rather than creating files
 * Use relative paths as far as possible for file paths
*/
File file = new File("." + File.separator + "1.txt");

common method

boolean exists()

Test whether the file or folder represented by this abstract path exists.

true denotes existence; false denotes nonexistence.

long length() Returns the length of the file represented by this abstract path
long lastModified() Returns the last time that the file represented by this abstract path was modified in milliseconds
String getName() Returns the name of the file or folder represented by this abstract path
String getPath() Returns the abstract path of the file or folder represented by this abstract path

boolean isFile()

boolean isDirectory()

Test whether the file represented by this abstract path is a standard file

Test whether the file represented by this abstract path is a standard folder

boolean createNewFile()

Create the file represented by this abstract path.

Returning true indicates successful creation; returning false indicates failed creation.

boolean delete()

Delete the file or folder represented by this abstract path

Returning true indicates that the deletion was successful; returning false indicates that the deletion failed.

Note: If this abstract path represents a folder whose folder contains subfiles or folders, you need to delete subfiles or subfolders before deleting the current folder.

boolean mkdir()

Create folders specified by this abstract path name (only one layer directory structure can be created, and mkdirs() method is required for multi-layer directory structure).

Returning true indicates successful creation; returning false indicates failed creation.

boolean mkdirs() Create the folder specified under this abstract path, including all necessary but non-existent parent directories. This method is recommended.
String[] list() Returns the name array of all subfiles and subfolders under this abstract path
File[] listFiles() Returns all subfiles and subfolder arrays under this abstract path
File[] listFiles(FileFilter fileter)

Returns subfiles and subfolders filtered by file filtering rules under this abstract path.

FileFilter is an interface, and you need to customize a class to implement it.

class MyFileFilter implements FileFilter{

// File filtering logic. Returning true preserves the current file or folder; returning false does not.
    public boolean accept(File pathname){
String fileName = pathname.getName(); // Get the current file name

// For example, if you decide that the filename ends with. txt, you return true, otherwise you return false.

            return fileName.endWith(".txt");
    }
}

Or use anonymous internal classes:

FIle[] subFiles = file.listFiles(new FileFilter(){

    public boolean accept(File pathname){
           String fileName = pathname.getName(); 

            return fileName.endWith(".txt");
    }

});

 

 

IO flow

IO Flow Concept

IO flow refers to Input flow and Output flow. Standing in memory as a reference, external file to memory is called Input, and internal file to external file is called Output.

Classification

  • Byte stream and character stream

IO streams can be divided into byte streams and character streams in terms of data transmission or transportation. The two differences are as follows:

1. Byte stream reads a single byte; character stream reads a single character (a character corresponds to different bytes according to the encoding). For example, UTF-8 encoding is 3 bytes and Chinese encoding is 2 bytes.
2. Byte streams are used to process binary files (such as pictures, MP3, video files, etc.) and character streams are used to process text files (which can be seen as special binary files, using some kind of coding, people can read).
3. Image analogy, bytes are for the computer to see, characters are for the user to see.
  • Node Flow and Processing Flow

IO flows can be divided into processing flows and node flows according to their functions.

Processing flow is used to pack node flow, and its function is strengthened. It is meaningless for processing flow to exist alone.

Processing Flow (Advanced Flow)

1.BufferedInputStream, BufferedOutputStream

Use such as: Buffered InputStream bfInput = new Buffered InputStream (InputStream in);

2.ObjectInputStream, ObjectOutputStream

3. Character Stream

Node Flow (Low Level Flow)

1.FileInputStream, FileOutputStream

  • Law
The stream ending in InputStream is a byte inflow and outflow; the stream ending in OutputStream is a byte output stream; the stream ending in Reader is a character input stream; and the stream ending in Writer is a character output stream.

Architecture

Common classes: the most basic read-write class for files beginning with File; the class with buffer for read-write of files beginning with Buffered; the class related to object serialization and deserialization beginning with Object.

As you can see from the figure above, the four most basic abstract classes of IO are InputStream, OutputStream, Reader and Writer.

Byte stream

InputStream

       InputStream Is the parent of all byte input streams, and isAbstract classes (not directly) new). The source code is as follows:

public abstract class InputStream implements Closeable {
    //MAX_SKIP_BUFFER_SIZE is used to determine the maximum buffer size to use when skipping.
    private static final int MAX_SKIP_BUFFER_SIZE = 2048;
    public abstract int read() throws IOException;
    public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }
    public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int c = read();
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;

        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }
    public long skip(long n) throws IOException {
        long remaining = n;
        int nr;

        if (n <= 0) {
            return 0;
        }

        int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
        byte[] skipBuffer = new byte[size];
        while (remaining > 0) {
            nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
            if (nr < 0) {
                break;
            }
            remaining -= nr;
        }

        return n - remaining;
    }
    public int available() throws IOException {
        return 0;
    }
    public void close() throws IOException {}
    public synchronized void mark(int readlimit) {}
    public synchronized void reset() throws IOException {
        throw new IOException("mark/reset not supported");
    }
    public boolean markSupported() {
        return false;
    }

}
  • common method
int read()

The input stream reads one byte at a time and returns the int value. The lower 8 bits are valid and the higher 24 bits are all complemented by 0.

Return - 1 indicates that the end of the file has been read.

int read(byte[] b)

From this input stream, read up to b.length bytes into the byte array b.

The return value represents the number of bytes actually read. If there is no more data because it has reached the end of the file, it returns - 1.

Decoding: The byte array is restored to a string according to the given character set encoding. If the encoding set is not specified, the default encoding set of the system is followed.

String s = new String(b, "encoding format (UTF-8, GBK, etc.)";

Note: The encoding set used for encoding and decoding should be consistent.

int read(byte[] b, int offset, int len) From then on, the input stream reads up to len bytes of data into a byte array.
void close() Close this input stream and release all system resources associated with it.
long skip(long n) Skip and discard n bytes of data in this input stream.

OutputStream

OutputStream is the parent of all byte output streams and is an abstract class (not new directly). The source code is as follows:

public abstract class OutputStream implements Closeable, Flushable {
    public abstract void write(int b) throws IOException;
    public void write(byte b[]) throws IOException {
        write(b, 0, b.length);
    }   
    public void write(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if ((off < 0) || (off > b.length) || (len < 0) ||
                   ((off + len) > b.length) || ((off + len) < 0)) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }
        for (int i = 0 ; i < len ; i++) {
            write(b[off + i]);
        }
    }
    public void flush() throws IOException {}
    public void close() throws IOException {}
}
  • common method
void write(int b) Write one byte at a time, and write the lower 8 bits of int.

void wtite(byte[] b)

 

Write an array of bytes at a time

Note String method: byte[] getBytes(String charsetName)

Converts a string to an array of bytes according to the given character set encoding.

Encoding: Converts a Unicode byte array into a byte array of the specified encoding set. If the encoding set is not specified, it is coded according to the default encoding set of the system. Windows default encoding set is GBK, Linux default encoding set is UTF-8.

byte[] b = s.getBytes("encoding format (UTF-8, GBK, etc.));

void write(byte[]b, int offset, int len) Starting from the offset position of the byte array, len bytes are written continuously into the output stream.
void flush() Refresh this output stream and force all buffered output bytes to be written.
void close() Close this output stream and release all system resources associated with it.

Closing flow

Close the file input stream and release all system resources associated with the stream, calling its close() method.

Notes

1. Closing the processing flow will automatically close its packaged node flow, so it only needs to close the processing flow.
2. The reason for the loss of data in file writing: For the output stream with buffer, the file will be written together when the buffer array is full, so the close() method must be called after the stream is used. The flush() method is called inside the close() method to force the remaining data in the cache array to be refreshed to the file.

For example, the close() method source code of ObjectOutputStream is as follows, calling the flush() method:

public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants{
    //......
    public void close() throws IOException {
        flush();
        clear();
        bout.close();
    }
}

 

Subclasses of InputStream and OutputStream

FileInputStream

FileInputStream is a file input stream used to obtain input bytes from a file in the file system. Read one byte at a time in bytes. For reading raw byte streams such as image data, to read character streams, use FileReader.

  • Source code
public class FileOutputStream extends OutputStream{
    //......
}
  • Construction method
FileInputStream(File file) Create a FileInputStream by opening a connection to the actual file, which is specified by the File object file in the file system.
FileInputStream(String name) Create a FileInputStream by opening a connection to the actual file, which is specified by the path name in the file system.
FileInputStream(FileDescriptor fdObj) Create a FileInputStream by using the file descriptor fdObj, which represents an existing connection to an actual file in the file system.

FileOutputStream

FileOutputStream is a file output stream that writes a byte to a file at a time in bytes. A stream for writing raw bytes such as image data. To write to the character stream, use FileWriter.

  • Source code
public class FileInputStream extends InputStream{
    //......
}
  • Construction method
FileOutputStream(File file) Creates a file output stream that writes data to a file represented by a specified File object. Default to override
FileOutputStream(String name) Creates an output file stream that writes data to a file with a specified name. Default to override
FileOutputStream(FileDescriptor fdObj) Creates an output file stream that writes data to the specified file descriptor, which represents an existing connection to an actual file in the file system.
FileOutputStream(File file, boolean append) Creates a file output stream that writes data to a file represented by a specified File object. In the form of additions.
FileOutputStream(String name, boolean append) Creates an output file stream that writes data to a file with a specified name. In the form of additions.

BufferedInputStream

When BufferedInputStream is created, an internal buffer array is created. When reading or skipping the bytes in the stream, the internal buffer can be filled again from the included input stream as needed, filling more than one byte at a time. The mark operation records a point in the input stream, and the reset operation causes all bytes read since the last mark operation to be read again before obtaining new bytes from the included input stream.

Read data into memory, and get data from memory is more efficient than from files.

  • Source code
public class BufferedInputStream extends FilterInputStream {
    private static int DEFAULT_BUFFER_SIZE = 8192;
    private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
    protected volatile byte buf[];
    private static final
        AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
        AtomicReferenceFieldUpdater.newUpdater
        (BufferedInputStream.class,  byte[].class, "buf");
    protected int count;
    protected int pos;
    protected int markpos = -1;
    protected int marklimit;

    private InputStream getInIfOpen() throws IOException {
        InputStream input = in;
        if (input == null)
            throw new IOException("Stream closed");
        return input;
    }

    private byte[] getBufIfOpen() throws IOException {
        byte[] buffer = buf;
        if (buffer == null)
            throw new IOException("Stream closed");
        return buffer;
    }

    public BufferedInputStream(InputStream in) {
        this(in, DEFAULT_BUFFER_SIZE);
    }

    public BufferedInputStream(InputStream in, int size) {
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }

    private void fill() throws IOException {
        byte[] buffer = getBufIfOpen();
        if (markpos < 0)
            pos = 0;            /* no mark: throw away the buffer */
        else if (pos >= buffer.length)  /* no room left in buffer */
            if (markpos > 0) {  /* can throw away early part of the buffer */
                int sz = pos - markpos;
                System.arraycopy(buffer, markpos, buffer, 0, sz);
                pos = sz;
                markpos = 0;
            } else if (buffer.length >= marklimit) {
                markpos = -1;   /* buffer got too big, invalidate mark */
                pos = 0;        /* drop buffer contents */
            } else if (buffer.length >= MAX_BUFFER_SIZE) {
                throw new OutOfMemoryError("Required array size too large");
            } else {            /* grow buffer */
                int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
                        pos * 2 : MAX_BUFFER_SIZE;
                if (nsz > marklimit)
                    nsz = marklimit;
                byte nbuf[] = new byte[nsz];
                System.arraycopy(buffer, 0, nbuf, 0, pos);
                if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
                    // Can't replace buf if there was an async close.
                    // Note: This would need to be changed if fill()
                    // is ever made accessible to multiple threads.
                    // But for now, the only way CAS can fail is via close.
                    // assert buf == null;
                    throw new IOException("Stream closed");
                }
                buffer = nbuf;
            }
        count = pos;
        int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
        if (n > 0)
            count = n + pos;
    }

    public synchronized int read() throws IOException {
        if (pos >= count) {
            fill();
            if (pos >= count)
                return -1;
        }
        return getBufIfOpen()[pos++] & 0xff;
    }

    private int read1(byte[] b, int off, int len) throws IOException {
        int avail = count - pos;
        if (avail <= 0) {
            /* If the requested length is at least as large as the buffer, and
               if there is no mark/reset activity, do not bother to copy the
               bytes into the local buffer.  In this way buffered streams will
               cascade harmlessly. */
            if (len >= getBufIfOpen().length && markpos < 0) {
                return getInIfOpen().read(b, off, len);
            }
            fill();
            avail = count - pos;
            if (avail <= 0) return -1;
        }
        int cnt = (avail < len) ? avail : len;
        System.arraycopy(getBufIfOpen(), pos, b, off, cnt);
        pos += cnt;
        return cnt;
    }

    public synchronized int read(byte b[], int off, int len)
        throws IOException
    {
        getBufIfOpen(); // Check for closed stream
        if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int n = 0;
        for (;;) {
            int nread = read1(b, off + n, len - n);
            if (nread <= 0)
                return (n == 0) ? nread : n;
            n += nread;
            if (n >= len)
                return n;
            // if not closed but no bytes available, return
            InputStream input = in;
            if (input != null && input.available() <= 0)
                return n;
        }
    }

    public synchronized long skip(long n) throws IOException {
        getBufIfOpen(); // Check for closed stream
        if (n <= 0) {
            return 0;
        }
        long avail = count - pos;

        if (avail <= 0) {
            // If no mark position set then don't keep in buffer
            if (markpos <0)
                return getInIfOpen().skip(n);

            // Fill in buffer to save bytes for reset
            fill();
            avail = count - pos;
            if (avail <= 0)
                return 0;
        }

        long skipped = (avail < n) ? avail : n;
        pos += skipped;
        return skipped;
    }

    public synchronized int available() throws IOException {
        int n = count - pos;
        int avail = getInIfOpen().available();
        return n > (Integer.MAX_VALUE - avail)
                    ? Integer.MAX_VALUE
                    : n + avail;
    }

    public synchronized void mark(int readlimit) {
        marklimit = readlimit;
        markpos = pos;
    }

    public synchronized void reset() throws IOException {
        getBufIfOpen(); // Cause exception if closed
        if (markpos < 0)
            throw new IOException("Resetting to invalid mark");
        pos = markpos;
    }

    public boolean markSupported() {
        return true;
    }

    public void close() throws IOException {
        byte[] buffer;
        while ( (buffer = buf) != null) {
            if (bufUpdater.compareAndSet(this, buffer, null)) {
                InputStream input = in;
                in = null;
                if (input != null)
                    input.close();
                return;
            }
            // Else retry in case a new buf was CASed in fill()
        }
    }
}

Where the FilterInputStream class inherits from InputStream

  • Construction method
BufferedInputStream(InputStream in) Create a Buffered Input Stream
BufferedInputStream(InputStream in, int size) Create Buffered InputStream with the specified buffer size
  • common method

See InputStream class

BufferedOutputStream

This class implements buffered output streams. By setting this output stream, the application can write each byte to the underlying output stream without calling the underlying system for each byte write.

  • Source code
public class BufferedOutputStream extends FilterOutputStream {
    protected byte buf[];
    protected int count;

    public BufferedOutputStream(OutputStream out) {
        this(out, 8192);
    }
    public BufferedOutputStream(OutputStream out, int size) {
        super(out);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }
    /** Flush the internal buffer */
    private void flushBuffer() throws IOException {
        if (count > 0) {
            out.write(buf, 0, count);
            count = 0;
        }
    }

    public synchronized void write(int b) throws IOException {
        if (count >= buf.length) {
            flushBuffer();
        }
        buf[count++] = (byte)b;
    }

    public synchronized void write(byte b[], int off, int len) throws IOException{
        if (len >= buf.length) {
            /* If the request length exceeds the size of the output buffer,
               flush the output buffer and then write the data directly.
               In this way buffered streams will cascade harmlessly. */
            flushBuffer();
            out.write(b, off, len);
            return;
        }
        if (len > buf.length - count) {
            flushBuffer();
        }
        System.arraycopy(b, off, buf, count, len);
        count += len;
    }

    public synchronized void flush() throws IOException {
        flushBuffer();
        out.flush();
    }
}

Where the FilterOutputStream class inherits from OutputStream

  • Construction method
BufferedOutputStream(OutputStream out) Create a new buffer output stream to write data to the specified underlying output stream.
BufferedOutputStream(OutputStream out, int size) Create a new buffer output stream to write data with the specified buffer size to the specified underlying output stream.
  • common method

See the OutputStream class

ObjectInputStream

  • concept

ObjectInputStream represents the object input stream. Deserialize basic data and objects written using ObjectOutputStream.  

  • Source code
public class ObjectInputStreamextends InputStream implements ObjectInput, ObjectStreamConstants{
    //......
}
  • Construction method
ObjectInputStream(InputStream in) Create ObjectInputStream written to the specified InputStream.
  • common method
Object readObjectj) Read objects from the output stream.

ObjectOutputStream

  • concept

ObjectOutputStream represents the object output stream, writes the basic data type of Java object into the OutputStream stream, and implements the object persistent storage by using files in the stream.

  • Source code
public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants{
    //......
}
  • Construction method
ObjectOutputStream(OutputStream out) Create ObjectOutputStream written to the specified OutputStream.
  • common method
void writeObject(Object obj) The object is then sent out to the output stream.

      

Character stream

Reader

       Reader Is the parent of all character input streams.

Writer

       Writer Is the parent of all character output streams.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Topics: encoding Java Windows Linux