• 018.day18 map集合如何实现排序 File类 IO流 字节流 字符流 编码


    文件操作和IO流

    复习

    一、map集合如何实现排序

    // TODO HashMap - key为一个引用类型(自定义类型)
    		// 定义Hash结构的Map集合
    		Map<Student, Integer> student = new HashMap<>();
    		// 根据存放顺序有序
    		student.put(new Student("sand", 18), 5);
    		student.put(new Student("tom", 19), 2);
    		student.put(new Student("tom", 19), 3);
    		student.put(new Student("jerry", 20), 4);
    		for (Map.Entry<Student, Integer> temp : student.entrySet()) {
    			System.out.println(temp);
    		}
    //Student [name=tom, age=19]=3
    //Student [name=jerry, age=20]=4
    //Student [name=sand, age=18]=5
    		// 将集合当中的元素重新排序
    		// 将一个Map结合中的键值对作为ArrayList的元素,直接转换为ArrayList
    		ArrayList<Map.Entry<Student, Integer>> list = new ArrayList<>(student.entrySet());
    		// list集合中存储顺序与map集合一致
    		for (Entry<Student, Integer> entry : list) {
    			System.out.println(entry);
    		}
    //Student [name=tom, age=19]=3
    //Student [name=jerry, age=20]=4
    //Student [name=sand, age=18]=5
    		// 借助Collections工具类中的sort方法
    		// 第一个参数为List接口的实现类
    		// 第二个参数为匿名内部类实现的比较器,或者是实现了外部比较器接口的类的实例
    		Collections.sort(list, new StudentComparator());
    		// 当前的排序结果记录在list当中
    		for (Entry<Student, Integer> entry : list) {
    			System.out.println(entry);
    		}
    //此时已经实现了排序,排序规则是在StudentComparator()中根据student类年龄升序排序
    //Student [name=sand, age=18]=5
    //Student [name=tom, age=19]=3
    //Student [name=jerry, age=20]=4
    
    public class StudentComparator implements Comparator<Entry<Student, Integer>> {
    	// 指定比较器的泛型为Map.Entry<Student, Integer>
    	@Override
    	public int compare(Entry<Student, Integer> o1, Entry<Student, Integer> o2) {
    		// o1代表当前对象,o2代表已经存在的对象
    		// Integer当中已经实现了自然排序接口,默认升序
    		// value中记录的是元素的存入顺序
    		// 可以直接调用compareTo方法使得集合按照map当中的value升序
    		// return o1.getValue().compareTo(o2.getValue());
    		// 针对key进行排序
    		// 如果key是一个自定义类型的实例,也可以在类的定义结构中实现自然排序接口,编写相应的比较规则,直接调用
    		return o1.getKey().compareTo(o2.getKey());
    	}
    
    }
    
    public int compareTo(Student o) {
    		// 需求:向TreeSet集合当中存放Student对象,使得对象之间根据年龄升序
    		// 当两个对象属性完全相同时,认定是重复对象,返回0
    		Student student = null;
    		// 1.判断实例的类型
    		if (o instanceof Student) {
    			student = (Student) o;
    		}else {
    			// 如果待比较元素不是Student类型,不予添加
    			return 0;
    		}
    		if (this.name.equals(student.getName()) && this.age == student.getAge()) {
    			// 当两个比较对象各属性相同时,认定相同,不予添加
    			return 0;
    		}
    		// 2.年龄的比较
    		if (this.age >= student.getAge()) {
    			// 通过正负来决定位置
    			// 正数放后
    			// 负数放前
    			return 1;
    		}else {
    			return -1;
    		}
    	}
    
    • 练习
    // TODO HashMap练习:1.根据商品价格升序/降序 2.根据存放顺序
    		Map<Product, Integer> map = new HashMap<>();
    		// 0.准备数据,构建集合
    		map.put(new Product("生活用品", 200), 1);
    		map.put(new Product("电子产品", 300), 2);
    		map.put(new Product("日常用品", 150), 3);
    		map.put(new Product("食品", 400), 4);
    		// 1.将集合转换为ArrayList
    		// 泛型与定义保持一致
    		ArrayList<Map.Entry<Product, Integer>> list = new ArrayList<>(map.entrySet());
    		// 2.自定义比较器,以一个实例的方式传入
    		Collections.sort(list, new ProductComparator());
    		// 3.输出结果
    		for (Map.Entry<Product, Integer> entry : list) {
    			System.out.println(entry);
    		}
    		
    		Collections.sort(list, new Comparator<Map.Entry<Product, Integer>>() {
    			// value当中记录了存放顺序
    			// Integer实现类自然排序接口
    			// 默认升序与value的顺序一致
    			@Override
    			public int compare(Entry<Product, Integer> o1, Entry<Product, Integer> o2) {
    				// 获得值
    				return o1.getValue().compareTo(o2.getValue());
    			}
    		});
    		System.out.println();
    		// 3.输出结果
    		for (Map.Entry<Product, Integer> entry : list) {
    			System.out.println(entry);
    		}
    
    • 数据分析的helloword
    // TODO 单词计数:统计每个单词出现的总次数
    		// 0.数据准备
    		String[] lines = new String[4];
    		lines[0] = "good good study";
    		lines[1] = "day day up";
    		lines[2] = "I am sand";
    		lines[3] = "I am a boy good";
    		// 1.遍历数据 -> 获取到每一个单词
    		Map<String, Integer> result = new HashMap<>();
    		for (String line : lines) {
    			for (String word : line.split(" ")) {
    				// word -> 获取到的每一个单词
    				// 2.当第一次添加时,次数记为1;后续添加时,累加次数
    				// result.get(word) == null
    				// 当集合当中不包含相应的key(统计的单词) -> 第一次添加进来 -> 次数记为1
    				if (!result.containsKey(word)) {
    					result.put(word, 1);
    				}else {
    					// 非首次添加,将次数进行累加
    					// int count = result.get(word) + 1;
    					// result.put(word,count)
    					result.put(word, result.get(word) + 1);
    				}
    			}
    		}
    		// 3.输出结果
    		for (Map.Entry<String, Integer> temp : result.entrySet()) {
    			System.out.println(temp);
    		}
    

    本节任务

    I/O流

    File类的使用

    字节流和字符流

    教学目标

    了解I/O流的概念

    掌握File类的使用

    掌握字节流的使用

    掌握字符流的使用

    教学内容

    一、File类

    在Java中,使用File类对磁盘上的文件(夹)进行操作

    1. 构造方法

    2. 字段

    • pathSeparator:与系统有关的路径分隔符,Windows下为分号,Linux下为冒号
    • pathSeparatorChar:同上,以一个字符的形式存在
    • separator:与系统有关的名称分隔符,Windows下为反斜杠,Linux下为斜杠
    • separatorChar:同上,以一个字符的形式存在

    3. 方法

    // TODO File类-文件路径
    		// 创建File的实例,其中包含了路径信息
    		// File的构造器,以字符串的形式指定路径
    		File file1 = new File("E://test");
    		// 文件(文件夹)的绝对路径
    		System.out.println(file1.getAbsolutePath());//E:	est
    		// 文件(文件夹)的标准路径
    		System.out.println(file1.getCanonicalPath());//E:	est
    		// "" -> 空字符串时能够代表当前路径 -> 可以获得绝对路径 -> 不能执行文件相关操作
    		// "文件名/路径" -> 从当前路径开始,拼接当前路径,获得一个完整的路径
    		// "/" -> 斜杠:当前能够到达的根目录 (Windows:所在盘符/Linux:根路径)
    		// 使用斜杠时以程序执行的位置有关,会获取当前所在盘符
    
    // TODO File类-构造器
    		File parent = new File("E://test");
    		// 直接传入File对象作为父级路径
    		File child1 = new File(parent, "b");
    		System.out.println(parent.getAbsolutePath());
    		System.out.println(child1.getAbsolutePath());
    		// 直接传入父级路径的字符串
    		File child2 = new File(parent.getPath(), "a");
    		System.out.println(child2.getAbsolutePath());
    
    // TODO File类 - 文件查看 - 练习:输出某一路径下的所有文件(夹)
    		File file = new File("E://test");
    		// 判断路径是否存在
    		if (!file.exists()) {
    			// 不存在则创建一个文件夹,返回值为boolean类型
    			file.mkdir();
    		}
    		// 获取当前路径下的所有文件(夹)
    		getFiles(file);
    	}
    	
    	/**
    	 * 递归的方式获得某一路径下的所有文件(夹)信息
    	 * @param file 指定某一个存在的路径
    	 */
    	public static void getFiles(File file) {
    		// 完全打印当前目录下的文件夹的内容
    		for (File child : file.listFiles()) {
    			// 打印当前目录下的子文件(夹)
    			System.out.println(child);
    		}
    		for (File child : file.listFiles()) {
    			// 判断该路径文件是否是一个文件夹
    			if (child.isDirectory()) {
    				// 如果子目录是一个文件夹,把其当成一个新的目录,遍历里面的子目录
    				getFiles(child);
    			}
    		}
    
    // TODO 路径相关的分隔符
    		// 各级路径之间的分隔符(文件(夹)与文件(夹)之间的分隔符)
    		// Windows下可以使用/(斜杠)和(反斜杠)
    		// Linux下只能使用斜杠
    		// 解决跨平台下的路径的兼容问题
    		System.out.println(File.separator);
    		// 路径与路径之间的分隔符
    		System.out.println(File.pathSeparator);
    
    // TODO File的删除 - 非空目录
    {
        File file = new File("E:/test");
    		if (!file.exists()) {
    			file.mkdirs();
    		}
    		delete(file);
    }
    public static void delete(File file) {
    		// 如果是空文件夹则直接删除
    		if (file.delete()) {
    			return;
    		}else {
    			// 非空文件夹
    			for (File child : file.listFiles()) {
    				// 尝试删除文件及文件夹
    				// 对于非空文件夹会进入if结构
    				// 即使不触发if,删除行为也会进行,例如:删除文件时删除成功,此时if中的值为false
    				if (!child.delete()) {
    					delete(child);
    				}
    			}
    			// 删除所传入路径的最外层文件夹
    			file.delete();
    		}
    
    // TODO File类的方法
    		File file = new File("E:/test");
    		System.out.println(file.canExecute());//执行:进入文件夹
    		System.out.println(file.canRead());//读->查看文件夹中的信息
    		System.out.println(file.canWrite());//写:修改文件夹内容
    		// 指定的路径文件不存在时,可以创建一个新的文件
    		File file2 = new File(file,"aa.txt");
    		file2.createNewFile();
    		// 创建所有不存在的父级目录
    		File file3 = new File(file,"qq/ww/ee");
    		file3.mkdirs();
    		// getParentFile() -> 父级路径的File对象,以最后一级目录作为当前路径,返回剩余的路径信息
    		System.out.println(file3.getParentFile().getAbsolutePath());//E:	estqqww
    		File file4 = new File("E:/");
    		// 当前路径已经在根目录,则返回null(返回空对象)
    		// getParent -> 获取父级路径的字符串
    		System.out.println(file4.getParentFile());//null
    

    二、IO流

    I:input,O:output,通常指对磁盘文件内容的读写操作,通过一个媒介或管道将文件与内存进行交互,这个媒介就被成为I/O流,流的本质为数据传输

    1. 按流向分类

    • 输入流:将数据读取到程序(内存)中使用的流
    • 输出流:从程序(内存)向外输出数据使用的流

    2. 按单位分类

    • 字符流:一次传输一个字符的数据,将数据以字符的形式传输
    • 字节流:一次传输一个字节的数据,将数据以字节的形式传输

    3. 按层次分类

    • 节点流:可以从/向一个特定的节点读写数据的流
    • 处理流:对已存在的流的封装,通过封装的流的功能调用实现数据读写

    三、字节流

    1. 字节输入流

    InputStream是一个抽象类,不能直接实例化对象

    • 相关方法

    // TODO 字节流 - 读取文件(输入流)
    		// 借助工具类 -> 每次读取若干个字节(一个或多个) -> 将读取到的数据转换为char类型输出原信息
    		// 中文字符需要编码处理
    		// 1.指定一个要读取的文件路径
    		File file = new File("E:/test.txt");
    		// 2.初始化相关的对象
    		InputStream inputStream = new FileInputStream(file);
    		int i = -1;
    		String result = "";
    		// 3.读取文件中所有的内容 - read()方法
    		// 赋值的同时进行判断 -> 将获取到的值记录在某一个变量中,判断时直接使用变量值判断,同时变量值可以正常使用
    		while((i = inputStream.read()) != -1) {
    			result += (char)i;
    		}
    		// 4.将读取到的信息输出
    		System.out.println(new String(result.getBytes("iso-8859-1"),"UTF-8"));
    		inputStream.close();
    

    2. 字节输出流

    OutputStream是抽象类,不能直接实例化对象

    // TODO 字节输出流
    		// 输出到磁盘的文件中
    		// 输出流重要参数:是否续写(apped),默认为flase
    		// 1.指定文件路径
    		File file = new File("E:/test.txt");
    		// 2.初始化字节输出流
    		OutputStream outputStream = new FileOutputStream(file,true);
    		// 
     -> 在Windows中使用
    作为换行符,Linux中使用
    
    		// 3.向路径中写入信息(以byte形式)
    		outputStream.write("
    789续写".getBytes());
    		outputStream.close();
    
    • 小练习

      • 字节流文件复制

        // TODO 字节流文件复制
        public static void main(String[] args) throws IOException {
        		copy("f:/test.txt", "f:/aa/testCopy.txt");
        	}
        
        	/**
        	 * 字节流文件复制
        	 * 
        	 * @param src
        	 *            源文件路径
        	 * @param dest
        	 *            目标文件路径
        	 */
        	public static void copy(String src, String dest) {
        		// 1.使用File指定文件路径
        		File srcFile = new File(src);
        		File destFile = new File(dest);
        		// 2.文件校验
        		// 源文件不存在,方法直接结束
        		if (!srcFile.exists()) {
        			System.out.println("源文件不存在");
        			return;
        		}
        		// 目标文件路径父级路径不存在
        		if (!destFile.getParentFile().exists()) {
        			destFile.getParentFile().mkdirs();
        		}
        		// 目标文件不存在则创建
        		if (!destFile.exists()) {
        			try {
        				destFile.createNewFile();
        			} catch (IOException e) {
        				e.printStackTrace();
        			}
        		}
        		// 3.初始化输入输出流
        		InputStream inputStream = null;
        		OutputStream outputStream = null;
        		try {
        			inputStream = new FileInputStream(srcFile);
        			outputStream = new FileOutputStream(destFile,false);
        		} catch (FileNotFoundException e) {
        			// TODO Auto-generated catch block
        			e.printStackTrace();
        		}
        		// 4.文件复制
        		// length代表本次读取到的字节数,-1时代表到达文件末尾
        		int length = -1;
        		// 将每次读取到的数据放入byte数组当中
        		// 使用字节数组的方式可以提高读写效率
        		byte[] data = new byte[1024];
        		try {
        			while ((length = inputStream.read(data)) != -1) {
        				// 写入byte数组中的数据,同时指定偏移量,从数组开头,一直到当前从源文件中读取的数据长度
        				outputStream.write(data,0,length);
        			}
        			// 5.关闭输入输出流
        			inputStream.close();
        			outputStream.close();
        		} catch (IOException e) {
        			e.printStackTrace();
        		}
        	}
        
      • 字节流练习:文件内容比较

        public static void main(String[] args) {
        		// TODO 字节流练习:文件内容比较
        		System.out.println(compare("E:/test.txt", "E:/testCopy.txt"));
        	}
        	
        	/**
        	 * 比较两个文件的内容是否完全一致
        	 * @param c1 比较文件A
        	 * @param c2 比较文件B
        	 * @return 两个文件是否相同
        	 */
        	public static boolean compare(String c1,String c2) {
        		// 1.使用File指定文件路径
        		File cFile1 = new File(c1);
        		File cFile2 = new File(c2);
        		// 2.文件校验
        		if (!cFile1.exists() || !cFile2.exists()) {
        			System.out.println("请检查源文件路径");
        			return false;
        		}
        		// 3.初始化输入流
        		InputStream inputStreamC1 = null;
        		InputStream inputStreamC2 = null;
        		try {
        			inputStreamC1 = new FileInputStream(cFile1);
        			inputStreamC2 = new FileInputStream(cFile2);
        		} catch (FileNotFoundException e) {
        			// TODO Auto-generated catch block
        			e.printStackTrace();
        		}
        		// 4.文件内容比较
        		int i = -1;
        		int j = -1;
        		// 结果初始值为true
        		boolean result = true;
        		try {
        			// 每次用i和j记录读取到的数据,当达到文件末尾时,值为-1
        			// &&/||逻辑运算有时会出现短路,当第一个结果已经能够决定整个表达式运算结果时,第二个表达式不会执行
        			// 可以使用位运算符
        			while ((i = inputStreamC1.read()) != -1 | (j = inputStreamC2.read()) != -1) {
        				// 每次读取到的字符进行比较
        				if (i != j) {
        					// 如果出现不相等,两个文件不相同,直接跳出
        					result = false;
        					break;
        				}
        			}
        			// 当某一个文件提前结束,i和j当中一定有一个为-1
        			// 此时i和j不相等
        			/*if (i != j) {
        				result = false;
        			}*/
        		} catch (IOException e) {
        			e.printStackTrace();
        		}
        		return result;
        	}
        

    3. 文件输入流

    以字节流的形式读取一个文件中的数据

    • FileInputStream:使用File作为参数初始化,指定要读取的文件

    • read()方法:每次读取一个字节

      // TODO 字符流输入流
      		// 1.初始化字符流
      		// 需要一个InputStream -> 实现类
      		Reader reader = new InputStreamReader(new FileInputStream(new File("f:/test.txt")));
      		// 2.读取文件中的数据
      		int i = -1;
      		while ((i = reader.read()) != -1) {
      			System.out.print((char)i);
      		}
      		// 3.关闭流
      		reader.close();
      
    // TODO 字符流一次读入多个字符
    		// 1.初始化字符流
    		// 需要一个InputStream -> 实现类
    		Reader reader = new InputStreamReader(new FileInputStream(new File("f:/test.txt")));
    		// 2.读取文件中的数据
    		// 当前次读取到的字符个数
    		int length = -1;
    		// 每次读取的最大字符数
    		char[] cs = new char[5];
    		while ((length = reader.read(cs)) != -1) {
    			// 最后一次(临近文件末尾)不一定会装满字符数组,此时以length为准
    			for (int j = 0; j < length; j++) {
    				System.out.print((char) cs[j]);
    			}
    		}
    		// 3.关闭流
    		reader.close();
    

    4. 文件输出流

    • FileOutputStream:使用File作为参数初始化,指定要写入的文件
    • write(byte[] b)方法:从指定byte数组中将数据写入此文件输出流中
    // TODO 字符流的输出流
    		// 1.初始化字符输出流
    		Writer writer = new OutputStreamWriter(new FileOutputStream(new File("E:/test.txt"),true));
    		// 2.使用字符流的方式写入数据
    		writer.write("!字符流输出!");
    		// 3.使用flush方法将缓冲区的数据写入文件
    		writer.flush();
    		// 流关闭时也会释放缓冲区
    		// 4.关闭流
    		writer.close();
    
    • 小练习
    // TODO 使用字符流实现文件的复制
    		copy("E:/test.txt", "E:/aa/bb/testCopy.txt");
    	}
    
    	public static void copy(String src,String dest) {
    		// 1.初始化文件路径
    		File srcFile = new File(src);
    		File destFile = new File(dest);
    		// 2.路径校验
    		if (!srcFile.exists()) {
    			System.out.println("源文件不存在");
    			return;
    		}
    		if (!destFile.getParentFile().exists()) {
    			// 自动创建父级目录
    			destFile.getParentFile().mkdirs();
    		}
    		// 3.初始化需要的流
    		Reader reader = null;
    		Writer writer = null;
    		try {
    			reader = new InputStreamReader(new FileInputStream(srcFile));
    			writer = new OutputStreamWriter(new FileOutputStream(destFile));
    		} catch (FileNotFoundException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		// 4.字符流方式文件复制
    		int length = -1;
    		char[] data = new char[5];
    		try {
    			while ((length = reader.read(data)) != -1) {
    				writer.write(data, 0, length);
    				writer.flush();
    			}
    			// 5.关闭输入输出流
    			reader.close();
    			writer.close();
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    

    四、字符流

    1. 输入流

    抽象父类:Reader

    • 子类:InputStreamReader,FileReader
    • 初始化字符输入流:需要一个文件流
    • 初始化文件流:需要一个File对象(包含路径信息)
    // 细化写法
    File file = new File("E:/test.txt");
    InputStream inputStream = new FileInputStream(file);
    Reader reader = new InputStreamReader(inputStream);
    // 简化写法
    Reader reader = new InputStreamReader(new FileInputStream(new File("E:/test.txt")));
    
    • read():每次读取一个字符(中英文均可),读取到文件末尾返回-1
    • 可以一次读取多个字符

    2. 字符输出流

    2. 输出流

    抽象父类:Writer

    • 子类:OutputStreamWriter,FileWriter
    • 初始化字符输出流:需要一个文件流
    • 初始化文件流:需要一个File对象(包含路径信息)
    // 细化写法
    File file = new File("E:/test.txt"); 
    OutputStream inputStream = new FileOutputStream(file); 
    Reader reader = new OutputStreamWriter(inputStream);
    // 简化写法
    Writer writer = new OutputStreamWriter(new FileOutputStream(new File("E:/test.txt")));
    
    • write(String str):可以直接向目标写入字符串
    • flush():刷新缓冲,对于有缓冲区的流都应调用该方法

    五、编码

    文本内容必须经过正确的编码和解码才能够正常显示,但有些时候我们可以通过ISO-8859-1编码来获得一个正确的表示

    • 字符串编码:getBytes(String charsetName)方法获得一个byte数组
    • 解码显示:new String(byte[] bytes,charsetName)
    • 解码方式和编码方式相同时,可以正常显示
    • UTF-8以及GBK格式经过ISO-8859-1解码再编码后,数据不丢失
    • UTF-8格式经过GBK解码再编码后,数据不丢失
  • 相关阅读:
    IE8上传插件jquery-form.js上传请求参数设置type为post失效问题
    路径参数汉字兼容问题
    vue-cli2移动端适配
    事件委托原生、jQuery实现
    new Date()在移动端的问题
    create-react-app配置less
    删除左右两边空格
    日期转换
    Git 常用命令
    单页面应用
  • 原文地址:https://www.cnblogs.com/yokii/p/9441840.html
Copyright © 2020-2023  润新知