22-1,键盘录入
键盘录入使用的是System.in,这个语句获取的是一个InputStream流对象,在通过这个对象的read()方法就可以获取键盘录入信息。
这个read()方法是阻塞式方法,也就是说,当JVM读到这个语句的时候,就会开始等待用户输入,直到用户输入结束符,才继续执行剩下的程序。
示例:
需求:读取一个键盘录入的数据,并打印在控制台上。
public class Demo { public static void main(String[] args) { InputStream in = System.in; int ch = in.read();//阻塞式方法,直到用户在控制台输入信息并回车,才继续执行下面的程序。 System.out.println(ch); int ch1 = in.read(); System.out.println(ch1); int ch2 = in.read(); System.out.println(ch2); //假如我们输入a,则打印97 13 10,因为回车是两个字符,所以打印三个结果 /* in.close();//会把系统流关闭,关闭后将无法从控制台获取数据,一般不要关闭 InputStream in2 = System.in;//需要重新获取流 int ch3 = int2.read(); */ } }
22-2,读取键盘录入练习
需求:获取用户在键盘录入的数据,并将数据变成大写打印在控制台上。如果用户输入的是over,则结束键盘录入。
思路:
(1)因为键盘录入只会读取一个字节,要判断是否是over,需要将读到的字符拼成字符串。
(2)既然要拼接字符串,就需要一个容器,使用StringBuilder。
(3)在用户回车之前将录入的数据变成字符串判断即可。
public class Demo { public static void main(String[] args) { //1.创建容器 StringBuilder sb = new StringBuilder(); //2.获取键盘读入流 InputStream in = System.in; //3.定义变量记录读取到的字节,并循环获取 int ch = 0; while((ch = in.read()) != -1) { //在存储之前要判断是否是换行标记,因为换行标记是不需要存储的 if(ch = ‘\r‘) continue; if(ch = ‘\n‘) { String temp = sb.toString(); if("over".equals(temp)) break; System.out.println(temp.toUpperCase()); sb.delete(0,sb.length()); } else { //将读到的字节存储到StringBuilder中 sb.append((char)ch); } } } }
22-3,转换流、字节流-->字符流、字符流-->字节流
1,上一节的方法中要读入一行数据,那么能否用类似于readLine()的方法呢?readLine()是BufferedReader中的方法,在字节流中无法使用。想要使用字符流的readLine()方法,需要将字节流转换为字符流。
2,InputStreamReader类是字节流通向字符流的桥梁,它使用指定的charset读取字节并将其解码为字符,它使用的字符集可以由名称指定或显示给定,或者可以接收平台默认字符集。
3,为了达到最高效率,可以考虑在BufferedReader内包装InputStreamReader,例如:
BufferedReaderin = new BufferedReader(new InputStreamReader(System.in));
4,示例
public class Demo { public static void main(String[] args) { //字节流 InputStream in = System.in; //将字节流转换成字符流的桥梁--InputStreamReader InputStreamReader isr = new InputStreamReader(in); //字符流,用Buffer提高效率 BufferedReader br = new BufferedReader(isr); String line = null; while((line = br.readLine())!=null) { if("over".equals(line)) break; System.out.println(line.toUpperCase()); } } }
5,中文字符为两个字节,用字节流读入的一个中文,只会读取一个字节,无法读取出中文,若用字节流转成字符流后再读取,则可以直接读取出中文,其实是因为InputStreamReader在读取完成后,在底层自动做了查编码表的操作,返回的是编码表上的字符,所以可以直接查出中文。
6,OutputStreamWriter类,是字符流通向字节流的桥梁,可以使用指定的charset将要写入流中的字符编码成字节。每次调用write方法都会导致在给定字符(或字符集)上调用编码转换器。在底层写入输出流之前,得到的这些字节将在缓冲区中累积。
这里提供一种高效包装的方法:
Writer out = new BufferedWriter(newOutputStreamWriter(System.in));
7,示例
public class Demo { public static void main(String[] args) { InputStream in = System.in; InputStreamReader isr = new InputStreamReader(in); BufferedReader bufr = new BufferedReader(isr); OutputStream out = System.out; OutputStreamWriter osw = new OutputStreamWriter(out); BufferedWriter bufw = new BufferedWriter(osw); String line = null; while((line = bufr.readLine()) != null) { if("over".equals(line)) break; bufw.write(line.toUpperCase()); bufw.newLine();//换行 bufw.flush();//从缓冲中刷新出去 } } }
8,转换流图解
9,高效装饰的简化版写法,尽量记住
public class Demo { public static void main(String[] args) { BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out)); String line = null; while((line = bufr.readLine()) != null) { if("over".equals(line)) break; bufw.write(line.toUpperCase()); bufw.newLine(); bufw.flush(); } } }
22-4,需求演示
1,需求一:将键盘录入的数据转成大写写入到一个文件中(当前目录的a.txt)。
public class Demo { public static void main(String[] args) { BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); //OutputStreamWriter需要接收一个字节流,且操作文件 //就想到了FileOutputStream,并将控制台录入的数据写入到a.txt中 BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"))); String line = null; while((line = bufr.readLine()) != null) { if("over".equals(line)) break; bufw.write(line.toUpperCase()); bufw.newLine(); bufw.flush(); } } }
2,需求二:将一个文本文件的内容显示在控制台上。
这里读入为一个文件,输出为控制台。
public class Demo { public static void main(String[] args) { //读入为一个文件,且需要字节流,使用FileInputStream, BufferedReader bufr = new BufferedReader(new InputStreamReader(new FileInputStream("a.txt"))); BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out)); String line = null; while((line = bufr.readLine()) != null) { if("over".equals(line)) break; bufw.write(line); bufw.newLine(); bufw.flush(); } } }
需求三:将一个文本文件中的内容复制到另一个文本文件中。
同上面的思路,改为:
public class Demo { public static void main(String[] args) { BufferedReader bufr = new BufferedReader(new InputStreamReader(new FileInputStream("a.txt"))); BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("b.txt"))); String line = null; while((line = bufr.readLine()) != null) { if("over".equals(line)) break; bufw.write(line); bufw.newLine(); bufw.flush(); } } }
22-5,流操作的基本规律
转换流:
InputStreamReader:字节到字符的桥梁,解码。
OuptutStreamWriter:字符到字节的桥梁,编码。
流的操作规律:
之所以要弄清楚这个规律,是因为流对象太多,开始时不知道用哪个对象合适。
想要知道开发时用哪个对象,只要通过四个明确即可知道。
四个明确:
(1)明确源和目的地(汇)
源:InputStream Reader
目的:OutputStream Writer
(2)明确数据是否是纯文本数据
源:是纯文本:Reader
不是纯文本:InputStream
目的:是纯文本:Writer
不是纯文本:OutputStream
到这里,就可以明确需求中具体要使用那个体系。
(3)明确具体设备
源设备:
硬盘:File
键盘:System.in
内存:数组
网络:Socket流
目的设备:
硬盘:File
键盘(控制台):System.out
内存:数组
网络:Socket流
到这里已经基本OK。
(4)是否需要其他额外功能?
1是否需要高效(缓冲区)?
是:则加上Buffer
2是否需要转换
是:则加上转换流
还有很多其他功能,如果需要,包装上即可。
22-6,四个明确练习-需求体现
1,需求1:赋值一个文本文件,用四个明确确定用那个对象。
(1)明确源和目的
源:InputStream Reader
目的:OutputStream Writer
(2)是否是纯文本?
是!
确定:源:Reader
目的:Writer
(3)明确具体设备
源:硬盘:File-->FileReader,FileInputStream
目的:硬盘:File-->FileWriter,FileOutputStream
使用:FileReader fr = newFileReader("a.txt")
FileWrtierfw = new FileWriter("b.txt")
(4)需要额外功能么?
需要高效!
BufferedReaderbufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriterbufw = new BufferedWriter(new FileWriter("b.txt"));
2,需求2:读取键盘录入的信息,并写入到一个文件中。
(1)明确源和目的:
源:InputStream Reader
目的:OutputStream Writer
(2)是否是纯文本:
是!
确定:源:Reader
目的:Writer
(3)明确设备
源:键盘:System.in
目的:硬盘:File
InputStreamin = System.in;
FileWriterfw = new FileWriter("b.txt");
这样做可以完成,但是麻烦,将读取的字节数据转成字符串,再由字符流操作。
(4)需要额外功能么?
需要:转换:将字节流转换成字符流,因为明确的源是Reader,这样操作文本数据更加便捷,所以要将已有的字节流转换成字符流,使用字节-->字符。InputStreamReader,此外,还需要高效处理,需要添加Buffer。
BufferedReaderbufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriterbufw = new BufferedWriter(new FileWriter("b.txt"));
22-7,需求体现2
1,需求3:将一个文本文件数据显示在控制台上。
前两个明确不变。
(3)明确具体设备
源:硬盘:File
目的:控制台:System.out
FileReaderfr = new FileReader("a.txt");
OutputStreamout = System.out;
(4)额外功能?
需要转换流,将获取的字符流转换成字节流,操作更加方便。
FileReaderfr = new FileReader("a.txt");
OutputStreamWriterosw = new OutputStreamWriter(System.out);
需要高效:
BufferedReaderbufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriterbufw = new BufferedReader(new OutputStreamWriter(System.out));
需求4:读取键盘录入的数据,显示在控制台上。
前两步相同。
(3)明确设备:
源:键盘:System.in
目的:控制台:System.out
InputStreamin = System.in;
OutputStreamout = System.out;
(4)明确额外功能?
转换:因为都是字节流,但是操作的却是文本数据,所以使用字符流更为便捷。
InputStreamReaderisr = new InputStreamReader(System.in);
OutputStreamWriterosw = new OutputStreamWriter(System.out);
高效:
BufferedReaderbufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriterbufw = new BufferedWriter(new OutputStreamWriter(System.out));
22-8,转换流的编码解码
1,用OutputStreamWriter或InputStreamReader的charset方法。
2,将一个中文字符串数据按照指定的编码表写入到一个文本文件中。
两种写出方式的比较:
(1)
FileWriterfw = new FileWriter("gbk_1.txt");
fw.write("你好");
fw.close();
(2)在写文件时指定编码表,用转换流指定。
OutputStreamWriterosw = new OutputStreamWriter(new FileOutputStream("gbk_3.txt","GBK"));
osw.write("你好");
osw.close();
这两个写法功能上是相同的。不同之处在于:
FileWriter:其实就是转换流指定了本机默认编码表的体现,而且这个转换流的子对象,可以方便操作文本文件,简单说:是操作文件的字节流+本机默认的编码表。这是按照默认编码表来操作文件的便捷类。
如果操作文件需要明确具体的编码,FileWriter就不行了,必须使用转换流。
3,用UTF-8写出"你好"
OutputStreamWriter osw = newOutputStreamWriter(new FileOutputStream("u8_1.txt"),"UTF-8");
osw.write("你好");
osw.close();
用UTF-8写一个中文字符占三个字节,GBK占两个字节。
4,用FileReader读取gbk_1.txt
FileReader fr = newFileReader("gbk_1.txt");
char[] buf = new char[10];
int len = fr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
fr.close();
gbk_1.txt是用GBK编码的,本例也用系统默认的GBK解码,故可以显示"你好"。
如果本例读u8_1.txt则会识别出不期望的文字,u8_1.txt用UTF-8编码成六个字节,先拿到前两个去GBK中查找,查出一个字符,再用中间两个查,再用后两个查,所以会出现三个字符。
5,只能用转换流读取UTF-8的文件:
InputStreamReader isr = newInputStreamReader(new FileInputStream("u8_1.txt"),"UTF-8");
char[] buf = new char[10];
int len = isr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
isr.close();
6,需求:将一个中文字符串数据按照指定的编码写入到一个文本文件中。
(1)目的:OutputStream Writer
(2)是纯文本:Writer
(3)设备:硬盘:File
FileWriterfw = new FileWriter("a.txt");
fw.write("你好");
注意:既然要求中已经明确了指定编码的操作,那就不可以使用FileWriter,因为FileWriter内部使用的是默认的编码表。只能使用其父类:OutputStreamWriter。OutputStreamWriter接收一个字符输出流对象,既然是操作文件,那么该对象应该是FileOutputStream。
OuptutStreamWriterosw = new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName),
需要高效:
BufferedWriter bufw = newBufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName));
什么时候使用转换流呢?
(1)源或目的对应的设备是字节流,但操作的却是文本数据,可用转换器作为桥梁,提高对文本操作的便捷。
(2)一旦操作的文本涉及到具体的指定编码时,必须用转换流。
22-9,File对象-构造函数&字段
1,File类。
用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作,File对象可以作为参数传递给流的构造函数,要了解File类中的常用方法。
2,构造器:
File(File parent,String child)
File(String pathname)
File(String parent,String child)
File(URI uri)
示例:
public class Demo { public static void main(String[] args) { //可将一个已存在的,或者不存在的文件或者目录封装成file对象 File f1 = new File("c:\\a.txt"); File f2 = new File("c:\\","a.txt"); File f = new File("c:\\"); File f3 = new File(f,"a.txt"); //用File.separator可以获取系统的名称分隔符 File f4 = new File("c:"+File.separator+"abc"+File.separator+"a.txt");//c:\\abc\a.txt } }
3,字段:
static String pathSeparator:与系统有关的路径分隔符,是个字符,比如环境变量中的path中的”;”,Unix中为”:”。
static String separator:名称分隔符,如文件路径中的”\”
static char pathSeparator:返回字符型
static char separator:返回字符型
22-10,File-常见功能-获取
public class Demo { public static void main(String[] args) { File file = new File("a.txt"); String name = file.getName();//获取文件名称 String absPath = file.getAbsolutePath();//绝对路径 String path = file.getPath(); long len = file.length();//获取大小 long time = file.lastModified();//最后修改时间 Date date = new Date(time); DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.LONG); String str_time = dateFormat.format(date);//格式化时间 } }
22-11,File-创建&删除
文件的创建和删除演示:
public class Demo { public static void main(String[] args) { File file = new File("file.txt"); //和输出流不一样,如果文件不存在,则创建,如果文件存在,则不创建。 boolean b = file.createFile();//创建 System.out.println(b); boolean b1 = file.delete();//删除 //创建单级目录(文件夹) File dir = new File("abc"); boolean b2 = dir.mkdir();//创建单级目录 //删除这个目录,如果这个目录内有别的文件或文件夹,则无法删除, //文件正在被操作时,也无法删除。 boolean b3 = dir.delete(); //创建多级目录(多层文件夹) File dirs = new File("abc\\a\\b\\c\\d\\e\\f\\g"); boolean b = dirs.mkdirs();//dirs对象此时封装的是最后一个文件夹g, dirs.delete();//删除的是文件夹g,windows的删除是从里向外删除的 } }
22-12,判断
判断指定的File是否存在,使用File的exists()方法。一般我们在创建一个文件的时候都会先判断一下这个文件是否存在,如果存在再创建,则会覆盖之前的,可能造成之前的数据丢失,为了数据安全,一般先判断一下。
public class Demo { public static void main(String[] args) { File f = new File("a.txt"); if(!f.exists()) { //如果这个文件不存在,则创建 f.createNewFile(); } boolean isFile = f.isFile();//判断指定File是不是一个文件 boolean isDirectory = f.isDirectory();//判断指定File是不是一个路径 } }
22-13,重命名
1,重命名
File f1 = new File("c:\\0.mp3"); File f2 = new File("c:\\9.mp3"); boolean b = f1.renameTo(f2);
以上操作会将c盘的0.mp3重命名为9.mp3。
2,如果是如下操作:
File f1 = new File("c:\\9.mp3"); File f2 = new File("d:\\aa.mp3"); boolean b = f1.renameTo(f2);
这个操作会将c盘的9.mp3剪切并重命名为aa.mp3。
22-14,系统根目录和容量获取
1,static File[] listRoots()
这时File中的一个静态方法,使用这个方法可以获取系统根路径的File对象数组。
其实获取的就是我们的c盘,d盘等。
2,获取容量
在获取了所有盘符以后,遍历File[]数组取出每一个盘,对每一个磁盘对象,可以获取其相关信息,例如:
public class Demo { public static void main(String[] args) { File[] files = File.listRoots(); for(File file : files) { System.out.println(file.getFreeSpace());//可用空间 System.out.println(file.getTotalSpace());//总空间 System.out.println(file.getUsableSpace());//可用空间 } } }
22-15,获取目录内容
String[] list();如果File代表一个文件夹,则这个方法会列出这个文件夹下的文件以及文件夹的名称,包括隐藏文件,例如:
public class Demo { public static void main(String[] args) { File file = new File("c:\\"); String[] names = file.list(); System.out.println(names.length);//目录中文件的个数没有则返回0 for(String name : names) { System.out.println(name); } } }
调用list方法的File对象中封装的必须是目录,否则会发生NullPointerException异常。如果访问的是系统级目录也会发生空指针异常。
22-16,文件过滤器
过滤器可以根据文件的一些属性信息来筛选文件,过滤掉我们不需要的文件。例如,我们可以根据扩展名获取文件的集合。
需求:取出c盘下所有后缀为.java的文件。
思路:File中的list方法只能打印指定目录下的全部文件名,要想按照指定条件读取需要的文件,就需要用到过滤器Filter。
File中有一个String[] list(FilenameFilter filter)方法,这个方法接收一个过滤器,可以根据这个过滤器筛选文件列表的名称。
public class Demo { public static void main(String[] args) { listDemo(); } public static void listDemo() { File dir = new File("c:\\"); //使用自定义的后缀过滤器 String[] names = dir.list(new SuffixFilter(".java")); for(String name : names) { System.out.println(name); } } }
//实现过滤器类,需要实现FilenameFilter文件名称过滤器接口 import java.io.File; import java.io.FilenameFilter; public class SuffixFilter implements FilenameFilter { private String suffix; public SufferFilter(String suffix) { super(); this.suffix = suffix; } //在这个接口中要实现accpet方法 public boolean accept(File dir,String name) { //如果满足下面这个条件,则是符合条件的文件。 //例如,要找出包含demo的文件名,return name.contains("demo")即可 return name.endswith(suffix); } }
2,过滤器Filter的实现过程
(1)指定盘符c:\\,并取得所有文件名,传入过滤器中。
(2)过滤器先调用list方法取出所有文件,封装到一个数组中,并遍历这个数组,取出符合accept中过滤条件的文件名,输出。
3,过滤器类必须实现一个过滤器接口,并实现accept方法。
4,取出所有非隐藏文件的文件名。
本例不是过滤文件名,不能用list方法取出名称了,这里还有一个更牛的方法listFiles(FileFilter filter),它返符合条件的文件对象的File[]数组,这里用到FileFilter文件过滤器。
实现:
public class Demo { public static void main(String[] args) { listDemo(); } public static void listDemo() { File dir = new File("c:\\"); File[] files = dir.listFiles(new FilterByHidden()); for(File file : files) { System.out.println(file); } } }
//编写文件过滤器类 public class FilterByHidden implements FileFilter { public boolean accept(File pathname) { return !pathname.isHidden(); } }