首先java io分为字节流和字符流:
字节流可以读非文本文件,如二进制文件,字符流只能读文本之类的文件,但字符流提供缓冲功能可以提高读取效率。
字符流指的是字符为缓冲区的大小,
字节流继承与inputstream outputstream
字符流继承于inputstreamreader outputstreamwriter
字符流是字节流的包装,在计算机中 所有的数据都必须以字节的形式存在,在字符流转为字节的时候,要注意编码的格式问题,
通过将字符串转换为字节数组。string->char[]
public static void main(String[] args) throws Exception {
String str = "中国人";
// 方式一【使用字节流输入,字节流输出。使用了字节缓冲区】
FileOutputStream fos = new FileOutputStream("D:\\1.txt");
fos.write(str.getBytes("UTF-8"));//将string转换为指定编码格式的byte[]
fos.close();
FileInputStream fr = new FileInputStream("D:\\1.txt");
byte[] buf = new byte[1024]; // 字节缓冲区 1k
int len;
String myStr = "";
while ((len = fr.read(buf)) != -1) {
myStr += new String(buf, 0, len, "UTF-8");
}
System.out.println(myStr);
// 方式二【使用字符流输入,字符流输出。使用了字符缓冲区】
FileWriter fw = new FileWriter("D:\\2.txt");
fw.write(str);
fw.close();
FileReader fr2 = new FileReader("D:\\2.txt");
char[] buf2 = new char[1024];
int len2 ;
String myStr2 = "";
while((len2 = fr2.read(buf2))!=-1){
myStr2 += new String(buf2, 0, len2);
}
System.out.println(myStr2);
// 方式三【使用字符流输入,字符流输出,没有使用缓冲区,直接读行】
PrintWriter pw = new PrintWriter("D:\\3.txt", "utf-8");
pw.write(str);
pw.close();
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("D:\\3.txt"), "utf-8"));
String myStr3 = br.readLine();
br.close();
System.out.println(myStr3);
}
默认的情况写入的是字节流没有缓冲区 但是字符流是有缓冲区的:
一种情况就是在输入流没有关闭的情况下面 字节流是往文件里面写入了数据的,但是字符流没有写入数据,而是保存在了缓冲区。缓冲区就是相当于是一段内存,当进行频繁的读取操作的时候,可以先保存在内存区域中,然后再对内存进行操作,在字符流的操作过程中,在输出前都是先保存在缓冲区里面,我们可以直接在不关闭输入流的情况下进行flush操作来清空缓存达到输入到文件中的效果。
提问:使用字节流好还是字符流好?
学习完字节流和字符流的基本操作后,已经大概地明白了操作流程的各个区别,那么在开发中是使用字节流好还是字符流好呢?
回答:使用字节流更好。
在回答之前,先为读者讲解这样的一个概念,所有的文件在硬盘或在传输时都是以字节的方式进行的,包括图片等都是按字节的方式存储的,而字符是只有在内存中才会形成,所以在开发中,字节流使用较为广泛。
字节流是最基本的,所有的InputStream和OutputStream的子类都是,主要用在处理二进制数据,它是按字节来处理的 但实际中很多的数据是文本,又提出了字符流的概念,它是按虚拟机的encode来处理,也就是要进行字符集的转化 这两个之间通过 InputStreamReader,OutputStreamWriter来关联,实际上是通过byte[]和String来关联 在实际开发中出现的汉字问题实际上都是在字符流和字节流之间转化不统一而造成的。
File是最基本的类:包括创建文件和删除文件,文件所在分区和所在路径以及获取文件名等一系列操作。
public static void createFile() {
File f = new File("E:/create.txt");
try {
f.createNewFile(); // 当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。
System.out.println("该分区大小" + f.getTotalSpace() / (1024 * 1024 * 1024) + "G"); // 返回由此抽象路径名表示的文件或目录的名称。
f.mkdirs();//创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。
// f.delete(); // 删除此抽象路径名表示的文件或目录
System.out.println("文件名 " + f.getName()); // 返回由此抽象路径名表示的文件或目录的名称。
System.out.println("文件父目录字符串 " + f.getParent());// 返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null。
} catch (Exception e) {
e.printStackTrace();
}
}
SequenceInputStream:有些情况下,当我们需要从多个输入流中向程序读入数据。此时,可以使用合并流,将多个输入流合并成一个SequenceInputStream流对象。SequenceInputStream会将与之相连接的流集组合成一个输入流并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。 合并流的作用是将多个源合并合一个源。其可接收枚举类所封闭的多个字节流对象。
/**
* @param args
* SequenceInputStream合并流,将与之相连接的流集组合成一个输入流并从第一个输入流开始读取,
* 直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
* 合并流的作用是将多个源合并合一个源。可接收枚举类所封闭的多个字节流对象。
*/
public static void main(String[] args) {
doSequence();
}
private static void doSequence() {
// 创建一个合并流的对象
SequenceInputStream sis = null;
// 创建输出流。
BufferedOutputStream bos = null;
try {
// 构建流集合。
Vector<InputStream> vector = new Vector<InputStream>();
vector.addElement(new FileInputStream("D:/text1.txt"));
vector.addElement(new FileInputStream("D:/text2.txt"));
vector.addElement(new FileInputStream("D:/text3.txt"));
Enumeration<InputStream> e = vector.elements();
sis = new SequenceInputStream(e);
bos = new BufferedOutputStream(new FileOutputStream("D:/text4.txt"));
// 读写数据
byte[] buf = new byte[1024];
int len = 0;
while ((len = sis.read(buf)) != -1) {
bos.write(buf, 0, len);
bos.flush();
}
} catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
} finally {
try {
if (sis != null)
sis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bos != null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
StreamTokenizer :
/**
* 统计字符数
* @param fileName 文件名
* @return 字符数
*/
public static void main(String[] args) {
String fileName = "D:/text1.txt";
TestRandomAccessFile.statis(fileName);
}
public static long statis(String fileName) {
InputStreamReader fileReader = null;
try {
fileReader = new InputStreamReader(new FileInputStream(fileName),"GBK");
// System.out.println(fileReader.getEncoding());
//创建分析给定字符流的标记生成器
StreamTokenizer st = new StreamTokenizer(new BufferedReader(
fileReader));
//ordinaryChar方法指定字符参数在此标记生成器中是“普通”字符。
//下面指定单引号、双引号和注释符号是普通字符
st.ordinaryChar('\'');
st.ordinaryChar('\"');
st.ordinaryChar('/');
String s;
int numberSum = 0;
int wordSum = 0;
int symbolSum = 0;
int total = 0;
//nextToken方法读取下一个Token.
//TT_EOF指示已读到流末尾的常量。
while (st.nextToken() !=StreamTokenizer.TT_EOF) {
//在调用 nextToken 方法之后,ttype字段将包含刚读取的标记的类型
switch (st.ttype) {
//TT_EOL指示已读到行末尾的常量。
case StreamTokenizer.TT_EOL:
break;
//TT_NUMBER指示已读到一个数字标记的常量
case StreamTokenizer.TT_NUMBER:
//如果当前标记是一个数字,nval字段将包含该数字的值
s = String.valueOf((st.nval));
System.out.println("数字有:"+s);
numberSum ++;
break;
//TT_WORD指示已读到一个文字标记的常量
case StreamTokenizer.TT_WORD:
//如果当前标记是一个文字标记,sval字段包含一个给出该文字标记的字符的字符串
s = st.sval;
System.out.println("单词有: "+s);
wordSum ++;
break;
default:
//如果以上3中类型都不是,则为英文的标点符号
s = String.valueOf((char) st.ttype);
System.out.println("标点有: "+s);
symbolSum ++;
}
}
System.out.println("数字有 " + numberSum+"个");
System.out.println("单词有 " + wordSum+"个");
System.out.println("标点符号有: " + symbolSum+"个");
total = symbolSum + numberSum +wordSum;
System.out.println("Total = " + total);
return total;
} catch (Exception e) {
e.printStackTrace();
return -1;
} finally {
if (fileReader != null) {
try {
fileReader.close();
} catch (IOException e1) {
}
}
}
}