Статьи

Java.io в двух словах: 22 тематических исследования

Этот пост пытается охватить полный набор операций в java.io. По сравнению с другими книгами и блогами, связанными с этой темой, моя мотивация состоит в том, чтобы показывать практические рекомендации с помощью тематических исследований. Будучи студентом Java, я понимаю, что наиболее эффективный способ изучения нового языка программы — это примеры: скопируйте и вставьте фрагмент кода, запустите его, чтобы увидеть результаты, затем попытайтесь изменить и повторно запустить его пошагово , Поэтому я предполагаю, что этот пост будет полезным.

Стоит отметить, что этот пост не будет касаться ничего, связанного с java.nio, потому что я думаю, что это совсем другая тема.

Содержание

Случай 0: создать новый файл

01
02
03
04
05
06
07
08
09
10
11
import java.io.File;
public class FileOperationTest {
  public static void main(String[] args) {
    File f = new File("helloworld.txt");
    try {
      f.createNewFile();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

Вывод: новый и пустой файл создается в рабочем каталоге, если до этого не было helloword.txt.

Случай 1: две константы в файле

1
2
3
4
5
6
7
import java.io.File;
public class FileOperationTest {
  public static void main(String[] args) {
    System.out.println(File.separator);
    System.out.println(File.pathSeparator);
  }
}

Выход:

1
2
/
:

Я получаю вывод выше, потому что я работаю на Linux. Если вы используете Windows, вывод должен быть \ и ; , Как можно видеть, с целью переносимости и надежности всегда рекомендуется использовать эти две константы.

Случай 2: Удалить файл

01
02
03
04
05
06
07
08
09
10
11
12
13
import java.io.File;
public class FileOperationTest {
  public static void main(String[] args) {
    File f = new File("helloworld.txt");
    if (f.exists()) {
      if (!f.delete()) {
        System.out.println("the file cannot be deleted.");
      }
    } else {
      System.out.println("the file does not exist.");
    }
  }
}

Случай 3: Создать каталог

1
2
3
4
5
6
7
import java.io.File;
public class FileOperationTest {
  public static void main(String[] args) {
    File f = new File("hello");
    f.mkdir();
  }
}

Случай 4: список файлов и каталогов в данном каталоге

1
2
3
4
5
6
7
8
9
import java.io.File;
public class FileOperationTest {
  public static void main(String[] args) {
    File f = new File(".");
    for (String str : f.list()) {
      System.out.println(str);
    }
  }
}

Вывод: я использую Eclipse

1
2
3
4
5
.settings
.classpath
.project
src
bin

Файл list () возвращает массив строк. Если вы предпочитаете массив File s, пожалуйста, используйте File . listFiles () :

1
2
3
4
5
6
7
8
9
import java.io.File;
public class FileOperationTest {
  public static void main(String[] args) {
    File f = new File(".");
    for (File subFile : f.listFiles()) {
      System.out.println(subFile.getName());
    }
  }
}

Случай 5: проверяет, является ли файл файлом

01
02
03
04
05
06
07
08
09
10
11
import java.io.File;
public class FileOperationTest {
  public static void main(String[] args) {
    File f = new File("helloworld.txt");
    if (f.isFile()) {
      System.out.println("YES");
    } else {
      System.out.println("NO");
    }
  }
}

В сочетании с файлом . listFiles () , мы можем перечислить все файлы в данном каталоге и его подкаталогах.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
import java.io.File;
 
public class FileOperationTest {
 
  public static void main(String[] args) {
    File f = new File(".");
    listFiles(f);
  }
 
  private static void listFiles(File f) {
    if (f.isFile()) {
      System.out.println(f.getName());
      return;
    }
    for (File subFile : f.listFiles()) {
      listFiles(subFile);
    }
  }
}

Выход: по сравнению с делом 4, чтобы увидеть разницу

1
2
3
4
5
org.eclipse.jdt.core.prefs
.classpath
.project
FileOperationTest.java
FileOperationTest.class

Случай 6: запись в файл RandomAccessFile

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
import java.io.IOException;
import java.io.RandomAccessFile;
 
public class FileOperationTest {
 
  public static void main(String[] args)
      throws IOException {
    RandomAccessFile file = new RandomAccessFile(
        "helloworld.txt", "rw");
    file.writeBytes("hello world!");
    file.writeChar('A');
    file.writeInt(1);
    file.writeBoolean(true);
    file.writeFloat(1.0f);
    file.writeDouble(1.0);
    file.close();
  }
}

Если вы откроете файл с помощью текстового редактора, вы найдете искаженный код, кроме первого « hello world!A (Обратите внимание на символ « A в конце «Привет, мир!»). Это связано с тем, что RandomAccessFile записывает в файл только массив байтов.

Случай 7: запись байтов в файл

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
 
public class FileOperationTest {
 
  public static void main(String[] args)
      throws IOException {
    OutputStream out = new FileOutputStream(
        "helloworld.txt");
    String str = "hello world!";
    out.write(str.getBytes());
    out.close();
  }
}

На этот раз вы можете увидеть «Привет, мир!» в файле. Конечно, вы можете записывать в байты OutputStream побайтно, но это менее эффективно:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
 
public class FileOperationTest {
 
  public static void main(String[] args)
      throws IOException {
    OutputStream out = new FileOutputStream(
        "helloworld.txt");
    String str = "hello world!";
    for (byte b : str.getBytes()) {
      out.write(b);
    }
    out.close();
  }
}

Случай 8: добавление байтов в файл

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
 
public class FileOperationTest {
 
  public static void main(String[] args)
      throws IOException {
    OutputStream out = new FileOutputStream(
        "helloworld.txt", true);
    String str = "hello world!";
    out.write(str.getBytes());
    out.close();
  }
}

Вывод: hello world!hello world!

Случай 9: чтение байтов из файла

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
 
public class FileOperationTest {
 
  public static void main(String[] args)
      throws IOException {
    InputStream in = new FileInputStream("helloworld.txt");
    byte[] bs = new byte[1024];
    int len = -1;
    while ((len = in.read(bs)) != -1) {
      System.out.println(new String(bs, 0, len));
    }
    in.close();
  }
}

InputStream . read () вернет -1, если достигнет конца файла. В противном случае он вернет общее количество байтов, считанных в буфер.

Случай 10: Копировать файлы

Просто объедините Case 7 и 9 , мы получим функцию копирования.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
 
public class Copy {
 
  public static void main(String[] args)
      throws IOException {
    if (args.length != 2) {
      System.out.println("java Copy SOURCE DEST");
      System.exit(1);
    }
    InputStream input = new FileInputStream(args[0]);
    OutputStream output = new FileOutputStream(args[1]);
    int len = 0;
    byte bs[] = new byte[1024];
    while ((len = input.read(bs)) != -1) {
      output.write(bs, 0, len);
    }
    input.close();
    output.close();
  }
}

Случай 11: запись символов в файл

01
02
03
04
05
06
07
08
09
10
11
12
13
14
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
 
public class FileOperationTest {
 
  public static void main(String[] args)
      throws IOException {
    Writer out = new FileWriter("helloworld.txt");
    String str = "hello world!";
    out.write(str);
    out.close();
  }
}

В приведенном выше случае вы получите тот же результат, что и в случае 7 . Так в чем же разница? FileWriter предназначен для написания потоков символов. Он будет использовать кодировку символов по умолчанию и размер байтового буфера по умолчанию. Другими словами, для удобства это класс-оболочка FileOutputStream . Поэтому, чтобы указать эти значения самостоятельно, рассмотрите возможность использования FileOutputStream .

Случай 12: чтение символов из файла

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
 
public class FileOperationTest {
 
  public static void main(String[] args)
      throws IOException {
    Reader in = new FileReader("helloworld.txt");
    char cs[] = new char[1024];
    int len = -1;
    while ((len = in.read(cs)) != -1) {
      System.out.println(new String(cs, 0, len));
    }
    in.close();
  }
}

Использовать ли потоки байтов или символов? Это действительно зависит. У обоих есть буферы. InputStream / OutputStream обеспечивают большую гибкость, но сделают вашу «простую» программу сложной. С другой стороны, FileWriter / FileReader дают удобное решение, но вы теряете контроль.

Случай 13: преобразование из OutputStream в FileWriter

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
 
public class FileOperationTest {
 
  public static void main(String[] args)
      throws IOException {
    Writer out = new OutputStreamWriter(
        new FileOutputStream("helloworld.txt"));
    out.write("hello world!");
    out.close();
  }
}

Вместо использования кодировки символов по умолчанию вы можете указать кодировку. Например,

1
2
Writer out = new OutputStreamWriter(
        new FileOutputStream("helloworld.txt"), "utf8");

Случай 14: Преобразование из InputStream в FileReader

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
 
public class FileOperationTest {
 
  public static void main(String[] args)
      throws IOException {
    Reader in = new InputStreamReader(
        new FileInputStream("helloworld.txt"));
    char cs[] = new char[1024];
    int len = -1;
    while ((len = in.read(cs)) != -1) {
      System.out.println(new String(cs, 0, len));
    }
    in.close();
  }
}

Случай 15: использование конвейера

Следующий код создает два потока: источник, который записывает что-то в конвейер на одном конце, и потребитель, который считывает это из конвейера на другом конце. Чтобы создать конвейер, нам нужно создать PipedInputStream и PipedOutputStream отдельно и соединить их с помощью output.connect(input) или через их конструкторы. В этой программе я намеренно запускаю поток Consumer сначала и прошу всю программу поспать 1 секунду перед запуском потока Producer. Это покажет, что конвейер работает. Стоит отметить, что я закрываю конвейер в источнике, потому что « поток, который пишет в поток, должен всегда закрывать OutputStream перед завершением. «Если мы удалим out.close() , будет out.close() IOException

1
2
3
4
5
6
java.io.IOException: Write end dead
    at java.io.PipedInputStream.read(PipedInputStream.java:311)
    at java.io.PipedInputStream.read(PipedInputStream.java:378)
    at java.io.InputStream.read(InputStream.java:101)
    at foo.Consumer.run(FileOperationTest.java:58)
    at java.lang.Thread.run(Thread.java:701)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
 
public class FileOperationTest {
 
  public static void main(String[] args)
      throws IOException, InterruptedException {
 
    PipedInputStream input = new PipedInputStream();
    PipedOutputStream output = new PipedOutputStream();
    output.connect(input);
 
    Producer producer = new Producer(output);
    Consumer consumer = new Consumer(input);
    new Thread(consumer).start();
    Thread.sleep(1000);
    new Thread(producer).start();
  }
}
 
class Producer implements Runnable {
 
  private final OutputStream out;
 
  public Producer(OutputStream out) {
    this.out = out;
  }
 
  @Override
  public void run() {
    String str = "hello world!";
    try {
      out.write(str.getBytes());
      out.flush();
      out.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
 
class Consumer implements Runnable {
 
  private final InputStream in;
 
  public Consumer(InputStream in) {
    this.in = in;
  }
 
  @Override
  public void run() {
    byte[] bs = new byte[1024];
    int len = -1;
    try {
      while ((len = in.read(bs)) != -1) {
        System.out.println(new String(bs, 0, len));
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

Случай 16: записать отформатированную строку в файл

PrintStream добавляет функциональность для удобной печати представлений различных значений данных. Синтаксис строки формата почти такой же, как C.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
 
public class FileOperationTest {
 
  public static void main(String[] args)
      throws IOException {
    PrintStream print = new PrintStream(
        new FileOutputStream("helloworld.txt"));
    print.printf("%s %s!", "hello", "world");
    print.close();
  }
}

Случай 17: перенаправить «стандартный» ввод-вывод

В Java и стандартный вывод, и вывод ошибок — PrintStream s. Стандартный ввод — InputStream . Поэтому мы можем переназначить их свободно. Следующий код перенаправляет стандартный вывод на вывод ошибок.

1
2
3
4
5
6
7
public class FileOperationTest {
  public static void main(String[] args) {
    System.out.println("hello world!");
    System.setOut(System.err);
    System.out.println("hello world!");
  }
}

Вывод: в Eclipse красный текст означает сообщение об ошибке

1
hello world!hello world!

Случай 18: чтение файла построчно

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
 
public class FileOperationTest {
 
  public static void main(String[] args)
      throws IOException {
    BufferedReader reader = new BufferedReader(
        new FileReader("helloworld.txt"));
    String str = null;
    while ((str = reader.readLine()) != null) {
      System.out.println(str);
    }
    reader.close();
  }
}

Случай 19: Сжать в ZIP-файл

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
 
public class FileOperationTest {
 
  public static void main(String[] args)
      throws IOException {
    ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(
        "helloworld.zip"));
    String str = "hello world!";
    for (int i = 0; i < 3; i++) {
      zipOut.putNextEntry(new ZipEntry("helloworld" + i + ".txt"));
      zipOut.write(str.getBytes());
      zipOut.closeEntry();
    }
    zipOut.close();
  }
}

Приведенный выше код создает zip-файл и помещает три файла с именами «helloworld0.txt», «helloworld1.txt» и «helloworld2.txt», каждый из которых содержит контент «hello world!».

Случай 20: выписка из архива

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.io.FileInputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
 
public class FileOperationTest {
 
  public static void main(String[] args)
      throws IOException {
    ZipInputStream zipIn = new ZipInputStream(new FileInputStream(
        "helloworld.zip"));
    ZipEntry entry = null;
    byte bs[] = new byte[1024];
    while ((entry = zipIn.getNextEntry()) != null) {
      // get file name
      System.out.printf("file: %s content: ", entry.getName());
      int len = -1;
      // read current entry to the buffer
      while((len=zipIn.read(bs)) != -1) {
        System.out.print(new String(bs, 0, len));
      }
      System.out.println();
    }
    zipIn.close();
  }
}

Выход:

1
2
3
file: helloworld0.txt content: hello world!
file: helloworld1.txt content: hello world!
file: helloworld2.txt content: hello world!

Случай 21: отодвинуть байты назад

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PushbackInputStream;
 
public class FileOperationTest {
 
  public static void main(String[] args)
      throws IOException {
    PushbackInputStream push = new PushbackInputStream(
        new ByteArrayInputStream("hello, world!".getBytes()));
    int temp = 0;
    while ((temp = push.read()) != -1) {
      if (temp == ',') {
        push.unread('.');
      }
      System.out.print((char) temp);
    }
  }
}

Приведенный выше код ставит точку после чтения запятой, поэтому вывод

1
hello,. world!

Однако, если вы попытаетесь отодвинуть больше символов назад, например push.unread("(...)".getBytes()); , вы получите IOException : Push Back буфер заполнен. Это связано с тем, что по умолчанию размер буфера pushback равен 1. Чтобы указать его с большей емкостью, используйте конструктор PushbackInputStream(InputStream in, int size) , например

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PushbackInputStream;
 
public class FileOperationTest {
 
  public static void main(String[] args)
      throws IOException {
    PushbackInputStream push = new PushbackInputStream(
        new ByteArrayInputStream("hello, world!".getBytes()), 10);
    int temp = 0;
    while ((temp = push.read()) != -1) {
      if (temp == ',') {
        push.unread("(...)".getBytes());
      }
      System.out.print((char) temp);
    }
  }
}

Выход:

1
hello,(...) world!