Java 文件IO续
文件IO续
File类
用来将文件和文件夹封装成对象 方便对文件和文件夹的属性信息进行操作
File对象可以作为参数传递给流的构造函数
Demo1 File的构造方法
public class FileDemo { public static void main(String[] args) { constructorDemo(); } public static void constructorDemo() { // 可以将一个已存在的,或者不存在的文件或者目录封装成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 f4 = new File("c:" + File.separator + "abc" + File.separator + "a.txt"); System.out.println(f4); } }
File类
用于将文件和文件夹封装成对象。
1,创建。
boolean createNewFile():如果该文件不存在,会创建,如果已存在,则不创建。不会像输出流一样会覆盖。
boolean mkdir();
boolean mkdirs();
2,删除。
boolean delete();
void deleteOnExit();
3,获取:
String getAbsolutePath();
String getPath();
String getParent();
String getName();
long length();
long lastModified();
4,判断:
boolean exists();
boolean isFile();
boolean isDirectory();
Demo2 File的常用方法
public class FileMethodDemo { public static void main(String[] args) throws IOException { getDemo(); createAndDeleteDemo(); isDemo(); renameToDemo(); listRootsDemo(); } public static void listRootsDemo() { File file = new File("d:\\"); System.out.println("getFreeSpace:" + file.getFreeSpace()); System.out.println("getTotalSpace:" + file.getTotalSpace()); System.out.println("getUsableSpace:" + file.getUsableSpace()); // File[] files = File.listRoots(); // for(File file : files){ // System.out.println(file); // } } public static void renameToDemo() { File f1 = new File("c:\\9.mp3"); File f2 = new File("d:\\aa.mp3"); boolean b = f1.renameTo(f2); System.out.println("b=" + b); } public static void isDemo() throws IOException { File f = new File("aaa"); // f.mkdir(); f.createNewFile(); // boolean b = f.exists(); // System.out.println("b="+b); // 最好先判断是否存在。 System.out.println(f.isFile()); System.out.println(f.isDirectory()); } public static void createAndDeleteDemo() throws IOException { File dir = new File("abc\\q\\e\\c\\z\\r\\w\\y\\f\\e\\g\\s"); // boolean b = dir.mkdir();//make directory // System.out.println("b="+b); // dir.mkdirs();//创建多级目录 System.out.println(dir.delete()); // System.out.println(dir.delete()); // 文件的创建和删除。 // File file = new File("file.txt"); /* * 和输出流不一样,如果文件不存在,则创建,如果文件存在,则不创建。 */ // boolean b = file.createNewFile(); // System.out.println("b="+b); // boolean b = file.delete(); // System.out.println("b="+b); } public static void getDemo() { // File file = new File("E:\\java0331\\day22e\\a.txt"); 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); System.out.println("parent:" + file.getParent()); System.out.println("name:" + name); System.out.println("absPath:" + absPath); System.out.println("path:" + path); System.out.println("len:" + len); System.out.println("time:" + time); System.out.println("str_time:" + str_time); } }
Demo3 文件浏览器
public class FileTest { public static void main(String[] args) { File dir = new File("E:\\ADT"); listAll(dir, 0); } public static void listAll(File dir, int level) { System.out.println(getSpace(level) + dir.getName()); // 获取指定目录下当前的所有文件夹或者文件对象 level++; File[] files = dir.listFiles(); for (int x = 0; x < files.length; x++) { if (files[x].isDirectory()) { listAll(files[x], level); } else System.out.println(getSpace(level) + files[x].getName()); } } private static String getSpace(int level) { StringBuilder sb = new StringBuilder(); sb.append("|--"); for (int x = 0; x < level; x++) { sb.insert(0, "| "); } return sb.toString(); } } }
Demo4 删除一个带文件的目录
public class RemoveDirTest { public static void main(String[] args) { File dir = new File("D:\\Java"); // dir.delete(); removeDir(dir); } public static void removeDir(File dir) { File[] files = dir.listFiles(); for (File file : files) { if (file.isDirectory()) { removeDir(file); } else { System.out.println(file + ":" + file.delete()); } } System.out.println(dir + ":" + dir.delete()); } }
Properties集合:
Map |--Hashtable |--Properties:
1,该集合中的键和值都是字符串类型。 2,集合中的数据可以保存到流中,或者从流获取。
3 通常该集合用于操作以键值对形式存在的配置文件。
Demo5 Properties的使用
public class PropertiesDemo { public static void main(String[] args) throws IOException { // methodDemo_4(); // myLoad(); test(); } // 对已有的配置文件中的信息进行修改。 /* * 读取这个文件。 并将这个文件中的键值数据存储到集合中。 在通过集合对数据进行修改。 在通过流将修改后的数据存储到文件中。 */ public static void test() throws IOException { // 读取这个文件。 File file = new File("E:\\info.txt"); if (!file.exists()) { file.createNewFile(); } FileReader fr = new FileReader(file); // 创建集合存储配置信息。 Properties prop = new Properties(); // 将流中信息存储到集合中。 prop.load(fr); prop.setProperty("wangwu", "16"); FileWriter fw = new FileWriter(file); prop.store(fw, ""); // prop.list(System.out); fw.close(); fr.close(); } // 模拟一下load方法。 public static void myLoad() throws IOException { Properties prop = new Properties(); BufferedReader bufr = new BufferedReader(new FileReader("info.txt")); String line = null; while ((line = bufr.readLine()) != null) { if (line.startsWith("#")) continue; String[] arr = line.split("="); // System.out.println(arr[0]+"::"+arr[1]); prop.setProperty(arr[0], arr[1]); } prop.list(System.out); bufr.close(); } public static void methodDemo_4() throws IOException { Properties prop = new Properties(); // 集合中的数据来自于一个文件。 // 注意;必须要保证该文件中的数据是键值对。 // 需要使用到读取流。 FileInputStream fis = new FileInputStream("info.txt"); // 使用load方法。 prop.load(fis); prop.list(System.out); } public static void methodDemo_3() throws IOException { Properties prop = new Properties(); // 存储元素。 prop.setProperty("zhangsan", "30"); prop.setProperty("lisi", "31"); prop.setProperty("wangwu", "36"); prop.setProperty("zhaoliu", "20"); // 想要将这些集合中的字符串键值信息持久化存储到文件中。 // 需要关联输出流。 FileOutputStream fos = new FileOutputStream("info.txt"); // 将集合中数据存储到文件中,使用store方法。 prop.store(fos, "info"); fos.close(); } /** * 演示Properties集合和流对象相结合的功能。 */ public static void methodDemo_2() { Properties prop = new Properties(); // 存储元素。 // prop.setProperty("zhangsan","30"); // prop.setProperty("lisi","31"); // prop.setProperty("wangwu","36"); // prop.setProperty("zhaoliu","20"); prop = System.getProperties(); prop.list(System.out); } /* * Properties集合的存和取。 */ public static void propertiesDemo() { // 创建一个Properties集合。 Properties prop = new Properties(); // 存储元素。 prop.setProperty("zhangsan", "30"); prop.setProperty("lisi", "31"); prop.setProperty("wangwu", "36"); prop.setProperty("zhaoliu", "20"); // 修改元素。 prop.setProperty("wangwu", "26"); // 取出所有元素。 Set<String> names = prop.stringPropertyNames(); for (String name : names) { String value = prop.getProperty(name); System.out.println(name + ":" + value); } } }
Demo6 获取一个应用程序运行的次数,如果超过5次,给出使用次数已到请注册的提示。并不要在运行程序
public class PropertiesTest { public static void main(String[] args) throws IOException { getAppCount(); } public static void getAppCount() throws IOException { // 将配置文件封装成File对象。 File confile = new File("E:\\count.properties"); if (!confile.exists()) { confile.createNewFile(); } FileInputStream fis = new FileInputStream(confile); Properties prop = new Properties(); prop.load(fis); // 从集合中通过键获取次数。 String value = prop.getProperty("time"); // 定义计数器。记录获取到的次数。 int count = 0; if (value != null) { count = Integer.parseInt(value); if (count >= 5) { // System.out.println("使用次数已到,请注册,给钱!"); // return; throw new RuntimeException("使用次数已到,请注册,给钱!"); } } count++; // 将改变后的次数重新存储到集合中。 prop.setProperty("time", count + ""); FileOutputStream fos = new FileOutputStream(confile); prop.store(fos, ""); fos.close(); fis.close(); } }
IO包中的其他类
打印流 PrintWriter PrintStream
1,提供了打印方法可以对多种数据类型值进行打印。并保持数据的表示形式。
2 它不抛IOException.
构造函数,接收三种类型的值 1,字符串路径 2,File对象。3,字节输出流
Demo7 PrintStream
public class PrintStreamDemo { public static void main(String[] args) throws IOException { PrintStream out = new PrintStream("E:\\info.txt"); // int by = read(); // write(by); // out.write(610);//只写最低8位, // out.print(97);//将97先变成字符保持原样将数据打印到目的地。 out.close(); } }
Demo8 PrintWriter
public class PrintWriterDemo { public static void main(String[] args) throws IOException { /* * PrintWriter:字符打印流。 构造函数参数: 1,字符串路径。 2,File对象。 3,字节输出流。 4,字符输出流。 */ BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); PrintWriter out = new PrintWriter(new FileWriter("out.txt"), true); String line = null; while ((line = bufr.readLine()) != null) { if ("over".equals(line)) break; out.println(line.toUpperCase()); // out.flush(); } out.close(); bufr.close(); } }
序列流 SequenceInputStream
Demo8 将1.txt 2.txt 3.txt文件中的数据合并到一个文件中。
public class SequenceInputStreamDemo { public static void main(String[] args) throws IOException { ArrayList<FileInputStream> al = new ArrayList<FileInputStream>(); for(int x=1; x<=3; x++){ al.add(new FileInputStream("E:\\" + x+".txt")); } Enumeration<FileInputStream> en = Collections.enumeration(al); SequenceInputStream sis = new SequenceInputStream(en); FileOutputStream fos = new FileOutputStream("E:\\1234.txt"); byte[] buf = new byte[1024]; int len = 0; while((len=sis.read(buf))!=-1){ fos.write(buf,0,len); } fos.close(); sis.close(); } }
Demo9 文件切割
public class SplitFileDemo { private static final int SIZE = 1024 * 1024; public static void main(String[] args) throws Exception { File file = new File("E:\\cloud.apk"); //splitFile(file); splitFile_2(file); } private static void splitFile_2(File file) throws IOException { // 用读取流关联源文件。 FileInputStream fis = new FileInputStream(file); // 定义一个1M的缓冲区。 byte[] buf = new byte[SIZE]; // 创建目的。 FileOutputStream fos = null; int len = 0; int count = 1; /* * 切割文件时,必须记录住被切割文件的名称,以及切割出来碎片文件的个数。 以方便于合并。 * 这个信息为了进行描述,使用键值对的方式。用到了properties对象 */ Properties prop = new Properties(); File dir = new File("E:\\partfiles"); if (!dir.exists()) dir.mkdirs(); while ((len = fis.read(buf)) != -1) { fos = new FileOutputStream(new File(dir, (count++) + ".part")); fos.write(buf, 0, len); fos.close(); } // 将被切割文件的信息保存到prop集合中。 prop.setProperty("partcount", count + ""); prop.setProperty("filename", file.getName()); fos = new FileOutputStream(new File(dir, count + ".properties")); // 将prop集合中的数据存储到文件中。 prop.store(fos, "save file info"); fos.close(); fis.close(); } public static void splitFile(File file) throws IOException { // 用读取流关联源文件。 FileInputStream fis = new FileInputStream(file); // 定义一个1M的缓冲区。 byte[] buf = new byte[SIZE]; // 创建目的。 FileOutputStream fos = null; int len = 0; int count = 1; File dir = new File("E:\\partfiles"); if (!dir.exists()) dir.mkdirs(); while ((len = fis.read(buf)) != -1) { fos = new FileOutputStream(new File(dir, (count++) + ".part")); fos.write(buf, 0, len); } fos.close(); fis.close(); } }
Demo10 合并文件
public class MergeFile { public static void main(String[] args) throws IOException { File dir = new File("E:\\partfiles"); mergeFile_2(dir); } public static void mergeFile_2(File dir) throws IOException { /* * 获取指定目录下的配置文件对象。 */ File[] files = dir.listFiles(new SuffixFilter(".properties")); if (files.length != 1) throw new RuntimeException(dir + ",该目录下没有properties扩展名的文件或者不唯一"); // 记录配置文件对象。 File confile = files[0]; // 获取该文件中的信息================================================。 Properties prop = new Properties(); FileInputStream fis = new FileInputStream(confile); prop.load(fis); String filename = prop.getProperty("filename"); int count = Integer.parseInt(prop.getProperty("partcount")); // 获取该目录下的所有碎片文件。 ============================================== File[] partFiles = dir.listFiles(new SuffixFilter(".part")); if (partFiles.length != (count - 1)) { throw new RuntimeException(" 碎片文件不符合要求,个数不对!应该" + count + "个"); } // 将碎片文件和流对象关联 并存储到集合中。 ArrayList<FileInputStream> al = new ArrayList<FileInputStream>(); for (int x = 0; x < partFiles.length; x++) { al.add(new FileInputStream(partFiles[x])); } // 将多个流合并成一个序列流。 Enumeration<FileInputStream> en = Collections.enumeration(al); SequenceInputStream sis = new SequenceInputStream(en); FileOutputStream fos = new FileOutputStream(new File(dir, filename)); byte[] buf = new byte[1024]; int len = 0; while ((len = sis.read(buf)) != -1) { fos.write(buf, 0, len); } fos.close(); sis.close(); } public static void mergeFile(File dir) throws IOException { ArrayList<FileInputStream> al = new ArrayList<FileInputStream>(); for (int x = 1; x <= 3; x++) { al.add(new FileInputStream(new File(dir, x + ".part"))); } Enumeration<FileInputStream> en = Collections.enumeration(al); SequenceInputStream sis = new SequenceInputStream(en); FileOutputStream fos = new FileOutputStream(new File(dir, "1.bmp")); byte[] buf = new byte[1024]; int len = 0; while ((len = sis.read(buf)) != -1) { fos.write(buf, 0, len); } fos.close(); sis.close(); } } class SuffixFilter implements FilenameFilter { private String suffix; public SuffixFilter(String suffix) { super(); this.suffix = suffix; } @Override public boolean accept(File dir, String name) { return name.endsWith(suffix); } }
ObjectInputStream 和 ObjectOutputStream
对象的序列化和反序列化。
writeObject readObject
Serializable标记接口
关键字:transient //非静态数据不想被序列化可以使用这个关键字修饰。
Demo11
public class ObjectStreamDemo { public static void main(String[] args) throws IOException, ClassNotFoundException { writeObj(); readObj(); } public static void readObj() throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\obj.object")); // 对象的反序列化。 Person p = (Person) ois.readObject(); System.out.println(p.getName() + ":" + p.getAge()); ois.close(); } public static void writeObj() throws IOException, IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\obj.object")); // 对象序列化。 被序列化的对象必须实现Serializable接口。 oos.writeObject(new Person("小强", 30)); oos.close(); } }
public class Person implements Serializable/*标记接口*/ { /** * transient:非静态数据不想被序列化可以使用这个关键字修饰。 */ private static final long serialVersionUID = 9527l; private transient String name; private static int age; public Person(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
RandomAccessFile
一看这个类名字,纠结。不是io体系中的子类。
1,该对象即能读,又能写。
2,该对象内部维护了一个byte数组,并通过指针可以操作数组中的元素,
3,可以通过getFilePointer方法获取指针的位置,和通过seek方法设置指针的位置
4,其实该对象就是将字节输入流和输出流进行了封装。
5,该对象的源或者目的只能是文件。通过构造函数就可以看出。
Demo12
public class RandomAccessFileDemo { public static void main(String[] args) throws IOException { // writeFile(); // readFile(); randomWrite(); } public static void randomWrite() throws IOException { RandomAccessFile raf = new RandomAccessFile("E:\\ranacc.txt", "rw"); // 往指定位置写入数据。 raf.seek(3 * 8); raf.write("哈哈".getBytes()); raf.writeInt(108); raf.close(); } public static void readFile() throws IOException { RandomAccessFile raf = new RandomAccessFile("ranacc.txt", "r"); // 通过seek设置指针的位置。 raf.seek(1 * 8);// 随机的读取。只要指定指针的位置即可。 byte[] buf = new byte[4]; raf.read(buf); String name = new String(buf); int age = raf.readInt(); System.out.println("name=" + name); System.out.println("age=" + age); System.out.println("pos:" + raf.getFilePointer()); raf.close(); } // 使用RandomAccessFile对象写入一些人员信息,比如姓名和年龄。 public static void writeFile() throws IOException { /* * 如果文件不存在,则创建,如果文件存在,不创建 */ RandomAccessFile raf = new RandomAccessFile("ranacc.txt", "rw"); raf.write("张三".getBytes()); raf.writeInt(97); raf.write("小强".getBytes()); raf.writeInt(99); // raf.close(); } }
管道流:需要和多线程技术相结合的流对象。
PipedOutputStream
PipedInputStream
Demo13
public class PipedStream { public static void main(String[] args) throws IOException { PipedInputStream input = new PipedInputStream(); PipedOutputStream output = new PipedOutputStream(); input.connect(output); new Thread(new Input(input)).start(); new Thread(new Output(output)).start(); } } class Input implements Runnable { private PipedInputStream in; Input(PipedInputStream in) { this.in = in; } public void run() { try { byte[] buf = new byte[1024]; int len = in.read(buf); String s = new String(buf, 0, len); System.out.println("s=" + s); in.close(); } catch (Exception e) { // TODO: handle exception } } } class Output implements Runnable { private PipedOutputStream out; Output(PipedOutputStream out) { this.out = out; } public void run() { try { Thread.sleep(5000); out.write("hi,管道来了!".getBytes()); } catch (Exception e) { // TODO: handle exception } } }
IO包中其它类
操作基本数据类型 DataInputStream DataOutputStream
Demo14
public class DataSteamDemo { public static void main(String[] args) throws IOException { writeData(); readData(); } public static void readData() throws IOException { DataInputStream dis = new DataInputStream(new FileInputStream("E:\\data.txt")); String str = dis.readUTF(); System.out.println(str); } public static void writeData() throws IOException { DataOutputStream dos = new DataOutputStream(new FileOutputStream("E:\\data.txt")); dos.writeUTF("你好"); dos.close(); } }
操作字节数组 ByteArrayInputStream ByteArrayOutputStream
Demo15
public class ByteArrayStreamDemo { public static void main(String[] args) { ByteArrayInputStream bis = new ByteArrayInputStream("abcedf".getBytes()); ByteArrayOutputStream bos = new ByteArrayOutputStream(); int ch = 0; while((ch=bis.read())!=-1){ bos.write(ch); } System.out.println(bos.toString()); } }
操作字符数组
操作字符串
编码表
计算机只能识别二进制数据 早期由来是电信号
为了方便应用计算机 让它可以识别各个国家的文字
就将各个国家的文字用数字来表示 并一一对应 形成一张表
就是编码表
常见编码表
ASCII 美国标准信息交换码 用一个字节的7位表示
ISO8858-1 拉丁码表 欧洲码表 用一个字节的8位表示
GB2312 中国的中文编码表
GBK 中国的中文编码表升级 加入更多中文字符
Unicode 国际标准码 用两个字节表示一个字符
UTF-8 最多用三个字节表示一个字符
字符串 --> 字节数组:编码
字节数组 --> 字符串:解码
你好:GBK: -60 -29 -70 -61
你好: utf-8: -28 -67 -96 -27 -91 -67
如果你编错了,解不出来
如果编对了,解错了,有可能有救
三个编码例子
Demo1
public class EncodeDemo { public static void main(String[] args) throws IOException { String str = "谢谢"; byte[] buf = str.getBytes("gbk"); String s1 = new String(buf, "UTF-8"); System.out.println("s1=" + s1); byte[] buf2 = s1.getBytes("UTF-8");// 获取源字节. printBytes(buf2);// -17 -65 -67 -17 -65 -67 -17 -65 -67 // -17 -65 -67 -17 -65 -67 -17 -65 -67 -17 -65 -67 // -48 -69 -48 -69 String s2 = new String(buf2, "GBK"); System.out.println("s2=" + s2); // encodeDemo(str); } public static void encodeDemo(String str) throws UnsupportedEncodingException { // 编码; byte[] buf = str.getBytes("UTF-8"); // printBytes(buf); // 解码: String s1 = new String(buf, "UTF-8"); System.out.println("s1=" + s1); } private static void printBytes(byte[] buf) { for (byte b : buf) { System.out.print(b + " "); } } }
Demo2
public class LianTong { public static void main(String[] args) throws IOException { String str = "联通"; /* 11000001 10101010 11001101 10101000 */ byte[] buf = str.getBytes("gbk"); for(byte b :buf){ System.out.println(Integer.toBinaryString(b&255)); } } }
Demo3
public class Test { public static void main(String[] args) throws IOException { String str = "ab你好cd谢谢"; // str = "ab琲琲cd琲琲"; // int len = str.getBytes("gbk").length; // for(int x=0; x<len; x++){ // System.out.println("截取"+(x+1)+"个字节结果是:"+cutStringByByte(str, x+1)); // } int len = str.getBytes("utf-8").length; for (int x = 0; x < len; x++) { System.out.println("截取" + (x + 1) + "个字节结果是:" + cutStringByU8Byte(str, x + 1)); } // String str = "琲"; // byte[] buf = str.getBytes("gbk"); // for(byte b : buf){ // System.out.println(b);//-84 105 // } } /* * 在java中,字符串“abcd”与字符串“ab你好”的长度是一样,都是四个字符。 但对应的字节数不同,一个汉字占两个字节。 * 定义一个方法,按照最大的字节数来取子串。 如:对于“ab你好”,如果取三个字节,那么子串就是ab与“你”字的半个, * 那么半个就要舍弃。如果去四个字节就是“ab你”,取五个字节还是“ab你”. */ public static String cutStringByU8Byte(String str, int len) throws IOException { byte[] buf = str.getBytes("utf-8"); int count = 0; for (int x = len - 1; x >= 0; x--) { if (buf[x] < 0) count++; else break; } if (count % 3 == 0) return new String(buf, 0, len, "utf-8"); else if (count % 3 == 1) return new String(buf, 0, len - 1, "utf-8"); else return new String(buf, 0, len - 2, "utf-8"); } public static String cutStringByByte(String str, int len) throws IOException { byte[] buf = str.getBytes("gbk"); int count = 0; for (int x = len - 1; x >= 0; x--) { if (buf[x] < 0) count++; else break; } if (count % 2 == 0) return new String(buf, 0, len, "gbk"); else return new String(buf, 0, len - 1, "gbk"); } }