java.io.File
:文件和文件目录路径的抽象表示形式,与平台无关File
能新建、删除、重命名文件和目录,但 File
不能访问文件内容本身。 如果需要访问文件内容本身,则需要使用输入/输出流File
对象;但是 Java程序 中的一个 File
对象,也可能没有一个真实存在的文件或目录File
对象可以作为参数传递给流的构造器File(File parent, String child)
File(String pathname)
File(String parent, String child)
File(URI uri)
@Test
方法,是相对于当前 Modulemain
方法,是相对于当前 Project\
来表示/
来表示File
类提供了一个常量:public static final String separator
← 根据操作系统,动态的提供分隔符public String getAbsolutePath()
:获取绝对路径public String getPath()
:获取路径public String getName()
:获取名称public String getParent()
:获取上层文件目录路径。若无,返回 null
public long length()
:获取文件长度(字节数)。不能获取目录的长度public long lastModified()
:获取最后一次的修改时间,毫秒值public String[] list()
:获取指定目录下的所有文件或者文件目录的名称数组public File[] listFiles()
:获取指定目录下的所有文件或者文件目录的 File
数组public boolean renameTo(File dest)
把文件重命名为指定的文件路径;如果 dest
已存在,falsepublic boolean isDirectory()
:判断是否是文件目录public boolean isFile()
:判断是否是文件public boolean exists()
:判断是否存在public boolean canRead()
:判断是否可读public boolean canWrite()
:判断是否可写public boolean isHidden()
:判断是否隐藏public boolean createNewFile()
:创建文件。若文件存在,则不创建,返回 falsepublic boolean mkdir()
:创建文件目录。如果此文件目录存在,就不创建了。 如果此文件目录的上层目录不存在,也不创建public boolean mkdirs()
:创建文件目录。如果上层文件目录不存在,一并创建public boolean delete()
:删除文件或者文件夹File
类中涉及到关于文件或目录的创建、删除、重命名、修改时间、文件大小等方法,并未涉及到写入或读取文件内容的操作,如果需要读取或写入文件内容,必须使用 [IO流] 来完成。后续 File
类的对象常会作为参数传递到流的构造器中,指明读取或写入的"端点"。
Input/Output
的缩写, I/O 技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等。java.io
包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据InputStream
和 Reader
是所有输入流的基类FileInputStream
和 FileReader
则是 InputStream
和 Reader
的典型实现public class FileInputStream extends InputStream
构造器
public FileInputStream(String name) throws FileNotFoundException
public FileInputStream(File file) throws FileNotFoundException
public class FileReader extends InputStreamReader
构造器
public FileReader(String fileName) throws FileNotFoundException
public FileReader(File file) throws FileNotFoundException
OutputStream
和 Writer
也非常相似,是所有输出流的基类FileOutputStream
和 FileWriter
public class FileOutputStream extends OutputStream
构造器
public FileOutputStream(String name) throws FileNotFoundException
public FileOutputStream(String name, boolean append) throws FileNotFoundException
public FileOutputStream(File file) throws FileNotFoundException
public FileOutputStream(File file, boolean append) throws FileNotFoundException
public class FileWriter extends OutputStreamWriter
构造器
public FileWriter(String fileName) throws IOException
public FileWriter(String fileName, boolean append) throws IOException
public FileWriter(File file) throws IOException
public FileWriter(File file, boolean append) throws IOException
FileOutputStream(file)
,则目录下有同名文件将被覆盖。如果使用构造器FileOutputStream(file, true)
,则目录下的同名文件不会被覆盖, 会在文件内容末尾追加内容。FileInputStream
从文件系统中的某个文件中获得输入字节。FileInputStream
用于读取非文本数据之类的原始字节流。要读取字符流,需要使用 FileReader
FileOutputStream
从文件系统中的某个文件中获得输出字节。FileOutputStream
用于写出非文本数据之类的原始字节流。要写出字符流,需要使用 FileWriter
.mp3 / .avi / .rmvb / .mp4 / .jpg / .doc / .ppt
.txt / .java / .c / .cpp
等语言的源代码。尤其注意 .doc / .excel / .ppt
这些不是文本文件
Writer
类中的 flush()
完成字节流是最基本的,所有的 InputStream
和 OutputStream
的子类都是主要用在处理二进制数据,它是按字节来处理的。但实际中很多的数据是文本,又提出了字符流的概念,它是按虚拟机的 encode 来处理,也就是要进行字符集的转化。这两个之间通过 InputStreamReader
,OutputStreamWriter
来关联,实际上是通过 byte[]
和 String
来关联。
在实际开发中出现的汉字问题实际上都是在字符流和字节流之间转化不统一而造成的。在从字节流转化为字符流时,实际上就是 byte[] → String
时,public String(byte bytes[], String charsetName)
。
有一个关键的参数字符集编码,通常我们都省略了,那系统就用操作系统的 lang。而在字符流转化为字节流时,实际上是 String → byte[]
时,byte[] String.getBytes(String charsetName)
也是一样的道理。至于 java.io
中还出现了许多其他的流,按主要是为了提高性能和使用方便,如BufferedInputStream
,PipedInputStream
等。
截取:《Java语言程序设计(基础篇)》
FileReader
是输入字符流,拷贝文件没问题,但拷贝图片就有问题了。
假设是在 Windows 下,FileReader
用的是 GBK 码表,一个字符最多用2个字节代表。2 个字节就是 2 的 16 次方,即有 65536 个格子范围,但 GBK 码表并没有将这些格子都用完,当读到某个二进制,假设是12421(我这里用二进制的十进制说明,二进制写起来太长)对应有码值“中”,那就读到完整的 2 个字节,数据是完整的。
但如果是另一个数字 21232 没有对应字符(码值),FileReader
读到这样的数据对应码表,找不到对应的字符,就会返回一个未知字符所对应的数字,占1个字节(返回值就是测试代码中的 content)。既然字节大小读不完整,FileWriter 写的时候还能正确吗?数据就是这样丢失的。
我在说“中”的时候大家不要蒙圈,读图片为什么要谈到码表对应的汉字。汉字只是图片中二进制数据在码表上的对应字符,它可以是汉字以外的其它字符代表都可以,对于 GBK 码表没有用完的格子,FileReader
读到的 content 就不是真实的数据。
由此我们也能知道字节流为什么能读取完整,因为它不需要码表,读到啥就得到啥,不会因为码表上没有对应字符就丢弃。
打一个很好的比方,用 FileReader
读图片,就像用记事本打开图片,因为记事本一遇到二进制数据就拿码表来“翻译”,可码表并不是每个格子都用到,容易导致数据丢失。
@Test
public void TestReaderAndWriter() {
// 1. File
File src = new File("src.txt");
File dest = new File("dest.txt");
// 2. FileReader, FileWriter
FileReader fr = null;
FileWriter fw = null;
try {
fr = new FileReader(src);
fw = new FileWriter(dest);
// 3. transfer
int len;
char[] cbuf = new char[1024];
while((len = fr.read(cbuf)) != -1)
fw.write(cbuf, 0, len);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4. close
if(fw != null)
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
if(fr != null)
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void copyByStream() {
// 1. File
File src = new File("src.png");
File dest = new File("dest.png");
// 2. FileReader, FileWriter
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(src);
fos = new FileOutputStream(dest);
// 3. transfer
int len;
byte[] cbuf = new byte[1024];
while((len = fis.read(cbuf)) != -1)
fos.write(cbuf, 0, len);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4. close
if(fos != null)
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
if(fis != null)
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
在我们学习字节流与字符流的时候,大家都进行过读取文件中数据的操作,读取数据量大的文件时,读取的速度会很慢,很影响我们程序的效率,那么,我想提高速度,怎么办?Java中提高了一套缓冲流,它的存在,可提高IO流的读写速度。
因为缓冲区技术是为流技术存在的,所以建立缓冲区之前必须先有流对象。然后把流对象作为参数传给缓冲对象的构造器。注意,缓冲类是没有空参构造器的,它必须在流对象的前提下创建。
缓冲流要“套接”在相应的节点流之上,根据数据操作单位可以把缓冲流分为:
BufferedInputStream
— 字节输入缓冲流
public BufferedInputStream(InputStream in)
public BufferedInputStream(InputStream in, int size)
BufferedOutputStream
— 字节输出缓冲流
public BufferedOutputStream(OutputStream out)
public BufferedOutputStream(OutputStream out, int size)
BufferedReader
— 字符输入缓冲流
public BufferedReader(Reader in)
public BufferedReader(Reader in, int sz)
BufferedWriter
— 字符输出缓冲流
public BufferedWriter(Writer out)
public BufferedWriter(Writer out, int sz)
缓冲区有:BufferedOutputStream/BufferedWriter
写入流的缓冲区和 BufferedInputStream/BufferedReader
读取流的缓冲区,因为 Writer
流对象和 Reader
流对象操作数据时要读一份写一份,而缓冲区能够把每次读入的数据存着,写的时候一次写出去。所以他们能够提高效率,原因是它底层会创建一个内部缓冲区数组。
BufferedInputStream 部份源码:
public class BufferedInputStream extends FilterInputStream {
private static int DEFAULT_BUFFER_SIZE = 8192;
// The internal buffer array where the data is stored.
protected volatile byte buf[];
// This is the index of the next character to be read from the buf array.
protected int pos;
// The index one greater than the index of the last valid byte in the buffer.
protected int count;
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];
}
}
缓冲区对象也一样可以操作 write()
,flush()
,close()
,并且当缓冲区 close()
时,它对应的流对象也会关闭。
特殊的是:BufferedWrite
缓冲区有 newLine()
,可以跨平台使用。Windows 的换行是 \r\n
,而 Linux 是 \n
,为了方便程序员写入统一的换行命令,Java 就封装了这个方法。相对应的,BufferReader
缓冲区有 readLine()
,可以读取一行的字符串,不含任何终止符。当读到文件末尾行以后,返回 null
,可以作为循环的控制条件。
测试代码1:
public void test() {
// 1. File
File srcFile = new File("src.png");
File destFile = new File("dest.png");
// 2. 节点流
FileInputStream fis = null;
FileOutputStream fos = null;
// 3. 处理流
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
// 4. transfer
byte[] buffer = new byte[1024];
int len;
while((len = bis.read(buffer)) != -1)
bos.write(buffer, 0, len);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 5. close
if(bos != null)
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
if(bis != null)
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
// fis.close(); // 处理流底层会去关闭节点流
// fos.close();
}
}
测试代码2:
public void test2() {
// 1. File
File srcFile = new File("BiTree.java");
File destFile = new File("二叉树.java");
// 2. 节点流
FileReader fr = null;
FileWriter fw = null;
// 3. 处理流
BufferedReader br = null;
BufferedWriter bw = null;
try {
fr = new FileReader(srcFile);
fw = new FileWriter(destFile);
br = new BufferedReader(fr);
bw = new BufferedWriter(fw);
// 4. transfer
/*
char[] cbuf = new char[1024];
int len;
while((len = br.read(cbuf)) != -1)
bw.write(cbuf, 0, len);
*/
String line;
while((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 5. close
if(br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
小结:
BufferedInputStream
读取字节文件时,BufferedInputStream
会一次性从文件中读取 8192个字节(8KB)
,存在缓冲区中,直到缓冲区装满了,才重新从文件中读取下一个 8192 个字节数组BufferedOutputStream
才会把缓冲区中的数据一次性写到文件里。使用 flush()
可以强制将缓冲区的内容全部写入输出流flush()
的使用:手动将 buffer 中内容写入文件close()
,不但会关闭流,还会在关闭流之前刷新缓冲区,关闭后不能再写出[转换流] 提供了在字节流和字符流之间的转换
InputStreamReader
,OutputStreamWriter
public class InputStreamReader extends Reader
InputStream
套接public class OutputStreamWriter extends Writer
OutputStream
套接@Test
public void transfer() throws IOException {
File srcFile = new File("utf-8.txt");
File destFile = new File("gbk.txt");
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
// 具体选用哪个字符集读入,取决于 File 保存时使用的字符集
InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
// 读入 UTF-8 编码的 File 将其copy一份再以 GBK 编码存储起来
OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");
char[] cbuf = new char[1024];
int len;
while((len = isr.read(cbuf)) != -1)
osw.write(cbuf, 0, len);
isr.close();
osw.close();
}
计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识 别各个国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表。 这就是编码表。
对于可变长度的编码集:当本字节最高位是0,说明这个字符就只用一个字节编码;如果字节最高位是1,说明这个字节和下一个字节合一起是一个字符的编码。
Unicode 不完美,这里就有三个问题,一个是,我们已经知道,英文字母只用 一个字节表示就够了,第二个问题是如何才能区别 Unicode 和 ASCII?计算机怎么知道两个字节表示一个符号,而不是分别表示两个符号呢?第三个,如果和 GBK 等双字节编码方式一样,用最高位是 1 或 0 表示两个字节和一个字节,就少了很多值无法用于表示字符,不够表示所有字符。Unicode 在很长一段时间内无法推广,直到互联网的出现。
面向传输的众多 UTF(UCS Transfer Format)标准出现了,顾名思义,UTF-8 就是每次 8 个位传输数据,而 UTF-16 就是每次 16 个位。这是为传输而设计的 编码,并使编码无国界,这样就可以显示全世界上所有文化的字符了。
Unicode 只是定义了一个庞大的、全球通用的字符集,并为每个字符规定了唯 一确定的编号,具体存储成什么样的字节流,取决于字符编码方案。推荐的 Unicode 编码是 UTF-8 和 UTF-16。
在标准 UTF-8 编码中,超出基本多语言范畴(BMP) 的字符被编码为 4 字节格式,但是在修正的 UTF-8 编码中,他们由 [代理编码对(surrogatepairs)] 表示,然后这些代理编码对在序列中分别重新编码。结果标准 UTF-8 编码中需要 4 个字节的字符,在修正后的 UTF-8 编码中将需要 8 个字节。
in
和 out
分别代表了系统标准的输入和输出设备
static PrintStream err
“标准”错误输出流static InputStream in
“标准”输入流;默认从键盘输入static PrintStream out
“标准”输出流;默认从控制台输出static void setIn(InputStream in)
重新分配“标准”输入流static void setOut(PrintStream out)
重新分配“标准”输出流练习:从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作,直至当输入“e”或者“exit”时,退出程序
public static void main(String[] args) {
BufferedReader br = null;
try {
// System.in → 转换流 → BufferedReader
br = new BufferedReader(new InputStreamReader(System.in));
String line;
while(true) {
System.out.print("Enter String: ");
line = br.readLine();
if("e".equalsIgnoreCase(line) ||
"exit".equalsIgnoreCase(line)) break;
String upperCase = line.toUpperCase();
System.out.println(upperCase);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
实现将基本数据类型的数据格式转化为字符串输出
打印流:PrintStream
和 PrintWriter
print()
和 println()
,用于多种数据类型的输出PrintStream
和 PrintWriter
的输出不会抛出 IOException
PrintStream
和 PrintWriter
有自动 flush 功能PrintStream
打印的所有字符都使用平台的默认字符编码转换为字节,在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter
类System.out
返回的是 PrintStream
的实例@Test
public void test() {
PrintStream ps = null;
try {
FileOutputStream fos = new FileOutputStream(new File("u:\\text.txt"));
// 创建打印输出流,设置为自动刷新模式(写入换行符或字节‘\n‘时都会刷新输出缓冲区)
ps = new PrintStream(fos, true);
if (ps != null) // 把标准输出流(控制台输出)改成文件
System.setOut(ps);
for (int i = 0; i <= 255; i++) {
// 输出ASCII字符
System.out.print((char) i);
if (i % 50 == 0) // 每50个数据一行
System.out.println(); // 换行
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (ps != null)
ps.close();
}
}
原文:https://www.cnblogs.com/liujiaqi1101/p/13340646.html