java基础(十一)

本次记录IO流,方便我们从各个方面存取数据。

IO流往简单学习就是输入流和输出流。再细分就是字节流还是字符流。常常都说,IO学五大类。而这些很多方法都是类似的,字符输入流有什么,那么字符输入流也有什么。相应的输出流也都有相对应的方法。所以也没那么难。

File

FIle文件操作的类,它不属于IO的四大类,但它是IO流的基础。怎么找文件,怎么判断是否为文件,才能方便我们后面的学习,怎么在文件上存和取。

在写代码前,我们先了解路径分隔符。
在win上分隔符是’\‘。如”D:\Test”。而’\‘在java中有特殊含义,如果要使用’\‘,需要再加’\‘转译’\‘,如”D:\\Test”。
而在linux上,分隔符是’/‘。”D:/Test”。不同系统有不同的分隔符。不过win也支持’/‘。所以我们在写路径可以采用linux的方法。
但java可能不止在win和linux上运行,所以我们可以用File里的方法separator来获取系统的分隔符。
如String path = “D:”+File.separator+”Test”;这段代码,如果路径比较长,还是挺麻烦的。但不会出错。

1
2
3
4
5
6
7
8
9
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
import java.io.File;											//记得引入File包
import java.io.IOException; //某些方法可能会抛出异常

public class FileTest {
public static void main(String[] args) throws IOException {

String path = "../../Test"; //相对路径
File src = new File(path);
System.out.println("是否在存在:"+src.exists());
System.out.println("是否为文件夹:"+src.isDirectory());
System.out.println("是否为文件:"+src.isFile());
System.out.println("是否可读:"+src.canRead());
System.out.println("是否可写:"+src.canWrite());

//一般情况,是文件夹,无论有没有内容,都得0,是文件获取大小
System.out.println("获得文件的大小:"+src.length());
System.out.println("相对于当前路径的绝对路径:"+src.getAbsolutePath());
System.out.println("纯绝对路径:"+src.getCanonicalPath());
System.out.println("传入的路径:"+src.getPath()); //根据你传入的路径,是什么就输出什么
System.out.println("当前文件|文件夹名称:"+src.getName());
System.out.println("上一级的路径:"+src.getParent());

System.out.println("是否创建01.txt文件:"+new File(path,"01.txt").createNewFile()); //File里可以写一个父路径和子路径,能拼接在一起

File[] url = src.listFiles(); //获取子文件的名称
for(File u:url ) {
System.out.println(u.getName());
}
System.out.println("-------------------------");
System.out.println("是否删除01.txt:"+new File(new File(path),"01.txt").delete()); //File里也也可以File加子路径的组合

url = src.listFiles();
System.out.println("是否创建C++/学习心得(mdir)"+new File(path,"C++/学习心得").mkdir()); //如果有多层路径,中间有不存在的路径则不会创建
System.out.println("是否创建C++/学习心得(mdirs)"+new File(path,"C++/学习心得").mkdirs()); //无论中间有没有路径,都一起创建出来
System.out.println("是否改变text,txt的名字:"+new File("../../Test/text.txt").renameTo(new File("../../Test/01.txt")));

url = src.listFiles();
for(File u:url) {
System.out.println(u.getName());
}

new File(path,"C++").delete();
new File("../../Test/01.txt").renameTo(new File("../../Test/text.txt"));
}
}


上面也有很多注意点,在获取文件夹的大小上,得到一个4096?百度查了一下,这可能是缓存的锅,按道理文件夹应该为0。只有文件才能获取大小。
要区别创建文件(createNewFile)还是文件夹(mkdirs)。创建文件夹推荐使用mkdirs。
这是最后运行后在我硬盘的内容。

而我们的循环只能获取一层,如果要获取全部文件,可以试试以下方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.io.File;

public class FileTest {
public static void main(String[] args) {
String path = "../../Test";
File src = new File(path);
findFile(src,"");
}
public static void findFile(File src,String separ)
{
if(src.exists())
{

System.out.print(separ);
System.out.println(src.getName());
if(src.isDirectory())
{
File[] url = src.listFiles();
for(File s:url)
{
findFile(s,separ+"|·");
}
}
}
}
}

编码

在记录四大IO类前,先解决一个问题——编码。我们常用的编码有GBK、utf-8、utf-16、ISO-8859-1、GB2312。每种编码格式不一样,如果编码和解码不统一,那么在操作上会出现乱码。
GBK:字母占一个字节,汉字占两个字节。
ISO-8859-1:字节编码。
GB2312:汉字编码,分成94个区,每个区有94位,每位对应一个字。
utf-8:变长unicode编码,字母占一字节,汉字占两字节。
utf-16:定长unicod编码,都占两个字节。还分大端(高字节在低地址)小端(低字节在低地址)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.io.UnsupportedEncodingException;				//会抛出异常

public class toBye {
public static void main(String[] args) throws UnsupportedEncodingException {
String str = "Hello世界";
//编码:
byte[] b = str.getBytes(); //获取文件编码格式,我这是GBK
System.out.println(b.length); //打印 9 字母占一个,汉字占两个

b = str.getBytes("GBK"); //把编码改为GBK
System.out.println(b.length);

b = str.getBytes("utf-8"); //把编码改为utf-8
System.out.println(b.length); //打印 11 字母占1个,汉字占三个

b = str.getBytes("utf-16BE"); //改为大端
System.out.println(b.length); //打印 14 每个字占两个字节

b = str.getBytes("utf-16"); //没有指定大端还是小端,
System.out.println(b.length); //打印 16,因为没指定,前两个字节表示该数组是大端还是小端

//解码:
str = new String(b,0,b.length,"utf-16"); //因为我们最后一次编码为utf-16
System.out.println(str); //打印 Hello世界
//乱码:
str = new String(b,0,b.length,"GBK"); //如果我们想用原本的编码格式GBK,会怎样
System.out.println(str); //打印一堆莫名奇妙的
}
}

节点流

从特点的节点来获取与读写数据,而这个节点可能是内存或硬盘等其他地方。
它以字节流和字符流两类。
字节流是一个字节或多个字节传输。一个字节占8bit。
字符流是一个字符或多个字符传输,一个字符占1~3字节,看编码格式。
字符流主要做文本的操作,而字节流还能做图片、音频、视频等等。字符流能做的字节流也能做。

字节流

我们可以从文件中获取内容,但是字节流是一个字节一个字节获取的。下面是读取文件里的内容。

1
2
3
4
5
6
7
8
9
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
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

public class InputStreamTest {
public static void main(String[] args) {
InputStream is = null; //创建输入字节流对象
int temp;
try {
is = new FileInputStream("test.txt"); //指定流位置,相对路径
// while((temp=is.read())!=-1) //文件没有了返回-1,不是null
// {
// System.out.println((char)temp); //每次读入一个字节,也只能一个一个打印
// }
byte[] b = new byte[100];
while((temp=is.read(b))!=-1) //读入多个字节
{
String src = new String(b,0,temp); //转为String可以打印多个字节,也可以循环打印
System.out.println(src);
}

} catch (FileNotFoundException e) { //可能抛出的异常
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally { //跟外界有交流,一定要记得关闭流
if(is!=null) {
try {
is.close(); //告诉虚拟机可以关闭流
} catch (IOException e) {
e.printStackTrace();
}
}
}

}
}

把内容写入到文件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class OutputStreamTest {
public static void main(String[] args) {
File src = new File("01.txt"); //在OutpuStream里,File不存在可以自动创建
try (OutputStream os = new FileOutputStream(src,true)){ //jdk1.7用try...with...resources来释放资源
//true 表示在之前的内容后添加,false表示重新写入
String str = "Hello,World\r\n";
byte[] b = str.getBytes(); //字节流输出,要用数组去接收
os.write(b,0,b.length);
os.flush(); //记得每次输出,要刷新一下,不要等close关闭后强制刷新
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

而网络中,数据都是以字节获取。而不是文件。所以我们通常是把文件转换成字节,在把字节转换成我们看得懂的字符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class Byte_IO {
public static void main(String[] args) {

byte[] b = "Hello,World".getBytes(); //要获取的字节数组流
InputStream is = new ByteArrayInputStream(b); //声明字节数组输入流的对象
ByteArrayOutputStream os = new ByteArrayOutputStream(); //声明字节数组输出流的对象,因为要用它写的方法,没用多态
int temp;
try {
while((temp=is.read())!=-1) {
os.write(temp); //获取is的数据
}
os.flush();
byte[] src = os.toByteArray(); //转为数组打印
System.out.println(new String(src,0,src.length));
} catch (IOException e) {
e.printStackTrace();
}
}
}

因为直接在网络或其他内存上使用,可以不用close()关闭流。

我们还有数据类型流,和对象流,这里就以数据类做例子,对象流类似。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class DateType {
public static void main(String[] args) {
try(OutputStream os = new FileOutputStream("003.txt");
DataOutputStream dos = new DataOutputStream(os);
InputStream is = new FileInputStream("003.txt");
DataInputStream dis = new DataInputStream(is); ) {

dos.writeUTF("你好"); //存数据到dos,dos写在了os里声明的003.txt文件里
dos.writeInt(18);
dos.writeBoolean(true);
dos.flush();
dos.writeChar('a');
System.out.println(dis.readUTF()); //取数据到dis,dis从is里声明的003.txt文件里


} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}

}
}


在这里,可不是乱码,是程序把我们写的内容序列化,给程序看的,但不影响我们的使用。而且,取数据,一定要按照存的顺序取。

字符流

用法和FileInputStream与FileOutputStream类似,但字符流传输的是一个或多个字符。

1
2
3
4
5
6
7
8
9
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
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;

public class ReaderAndWriter {
public static void main(String[] args) {
try (Writer writer = new FileWriter("a.txt");
Reader reader = new FileReader("a.txt");){
//writer的使用
writer.write("Hello,World\r\n"); //我们可以传入字符串
writer.flush();

char[] b = "大家好".toCharArray(); //也可以传入字符数组
writer.write(b,0,b.length);
writer.flush();

writer.append("\r\n").append("欢迎来我的博客"); //或者writer独有的append方法去添加
writer.flush();

//reader的使用
int length;
char[] src = new char[100];
while((length=reader.read(src))!=-1) //和InputStream类似
{
String str = new String(src,0,length);
System.out.println(str);
}


} catch (IOException e) {

e.printStackTrace();
}
}
}

缓冲流

缓冲流是提高效率的,上面的方法,每一次取,都要去原文件里拿。就好比一个加工厂,我每次只拿我本次用的材料,下次还用又从仓库里取下次够用的材料。所以每次取都要跑到仓库里。而缓冲流的效果就是每次从仓库拿一堆材料,每次用都在旁边取,如果不够了,再从仓库拿一堆。这提高了运行效率。

字节流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Buffered_IO {
public static void main(String[] args) {
try(OutputStream os = new BufferedOutputStream( //声明缓冲流对象
new FileOutputStream( //声明文件操作
new File("001.txt"),false)); //声明文件位置,
//FileOutputStream(File,boolean)默认为false
InputStream is = new BufferedInputStream(
new FileInputStream(
new File("001.txt")));) {

byte[] b = "Hello,World".getBytes();
os.write(b,0,b.length); //写入到001.txt
os.flush(); //强制刷新,缓冲流不到一定内容,不会写入

byte[] a = new byte[5]; //每次写入多少字节
int len=0;
while((len=is.read(a))!=-1) {
System.out.println(new String(a,0,len)); //打印获取的内容
}

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}

字符流

字符流中的缓冲流,本身有很多新的方法,所以建议不要多态。

1
2
3
4
5
6
7
8
9
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
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Buffered_IO02 {
public static void main(String[] args) {
try (BufferedWriter writer = new BufferedWriter(
new FileWriter("002.txt",false)); //默认为false,重新写入
BufferedReader reader = new BufferedReader(
new FileReader("002.txt"));){
//writer的使用
writer.write("Hello,World\r\n"); //我们可以传入字符串
writer.flush();

char[] b = "大家好".toCharArray(); //也可以传入字符数组
writer.write(b,0,b.length);
writer.flush();
writer.newLine(); //写入一个换行
writer.append("欢迎来我的博客"); //或者writer独有的append方法去添加
writer.flush();

//reader的使用
// int length;
// char[] src = new char[100];
// while((length=reader.read(src))!=-1) //和InputStream类似
// {
// System.out.println(new String(src,0,length));
// }
String src=null;
while((src=reader.readLine())!=null) { //可以每次读取一行数据
System.out.println(src);
}

} catch (IOException e) {

e.printStackTrace();
}
}
}

转换流

因为网络中都是字节传输,而我们实际可能是想要字符操作,所以我们可以把字节流转换成字符流。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class Change {
public static void main(String[] args) {
try (BufferedReader reader = new BufferedReader( //先建立缓冲流提高效率
new InputStreamReader(System.in,"GBK")); //获得字节输入并转换为字符,也可以指定编码
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(System.out,"GBK"));){ //字符输出,也可以指定编码
writer.write(reader.readLine()); //读一行字符并写入
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}

但是在指定编码,一定要与自己写的代码编码一致,我这是GBK,所以才写GBK,如果是utf8就写utf8。

还有很多类和方法,都是大同小异。

谢谢您对我的支持
0%