面试题之——java交织读取两个文件中单词,然后写入新的文件

面试题之——java交叉读取两个文件中单词,然后写入新的文件

今天看到一道面试题,题目为:编写一个程序,将a.txt文件中的单词与b.txt文件中的单词进行交替合并到c.txt文件中 。a.txt文件中的单词用回车符分隔,b.txt文件中用回车或空格分开。

文章中给出的代码是

  1. import java.io.File;
    import java.io.FileReader;
    import java.io.FileWriter;
    
    
    public class MainClass {
    	public static void main(String[] args) throws Exception {
    		FileManager a = new FileManager("a.txt", new char[]{'\n'});
    		FileManager b = new FileManager("b.txt", new char[]{'\n', ' '});
    		FileWriter c = new FileWriter("c.txt");
    		String aWord = null;
    		String bWord = null;
    		while((aWord=a.nextWord())!=null) {
    			c.write(aWord + "\n");
    			bWord = b.nextWord();
    			if(bWord!=null) {
    				c.write(bWord + "\n");
    			}
    		}
    		while((bWord = b.nextWord())!=null) {
    			c.write(bWord + "\n");
    		}
    		c.close();
    	}
    }
    
    class FileManager {
    	String[] words = null;
    	int pos = 0;
    	
    	public FileManager(String filename, char[] seperators) throws Exception{
    		File f = new File(filename);
    		FileReader reader = new FileReader(f);
    		char[] buf = new char[(int)f.length()];
    		int len = reader.read(buf);
    		String result = new String(buf, 0, len);
    		String regex = null;
    		if(seperators.length>1) {
    			regex = "" + seperators[0] + "|" + seperators[1];
    		} else {
    			regex ="" + seperators[0];
    		}
    		words = result.split(regex);
    	}
    	
    	public String nextWord() {
    		if(pos == words.length) {
    			return null;
    		}
    		return words[pos++];
    	}
    }
    


代码不足之处

  代码本身能够做到题目的要求,但是有几处考虑不足:

  1. 在FileManager类中,我们使用FileReader字符流打开了资源,而在程序最后并没有关闭该资源,这是十分危险的操作。
  2. 在使用FileReader对文件进行读取时,只是简单的读取了一次就把读取到的数据封装成了String字符串。如果我们要读取的文件较小,只有几KB,那没有问题。那么,当我们读取的文件很大,大到一次并不能读取完,这是就出问题了。
  3. (这处也许是我多虑了)我们都知道,Linux和windows下的文件在回车换行方面处理不同。“\r,\n”都有新起一行的效果,但在两种操作系统中处理方式不同,也就是说,在windows下当我们敲下回车键时,在新起的上一行的最后会自动添加“\r\n”,而Linux下只会添加“\n”。此时就体现出了操作系统的差异性。那么我们对待这两种操作系统要有不同的操作。同时,在我们向新文件即c.txt文件中写入时,如果使用的是c.write(aWord + "\n");,那么在Windows下打开该文件,所有的单词应该显示成一行,而Linux操作系统下则会一个单词一个换行。

对上述不足改进

添加对资源进行关闭的方法

public void close() {
		if(fis!=null) {  //fis是读取文件的字节流
			try {
				fis.close();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				fis = null;
			}
		}
	}

对读取文件内容进行改进

byte[] a = new byte[fis.available()];
		int len = fis.read(a);
		int total = 0;
		//如果total小于字节数组的长度,说明文件没有读取完成
		while (total < a.length) {
			total += len;
			//向字节数组中写入数据,起始偏移量是已读取数据长度, a.length - total表示剩余数据长度
			len = fis.read(a, total, a.length - total);
		}


改进后代码

package com.zyh.interview;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;

/**
 * 将a.txt文件中的单词与b.txt文件中的单词进行交替合并到c.txt文件中 
 * a.txt文件中的单词用回车符分隔,b.txt文件中用回车或空格分开
 * @author zyh
 */
public class Question111 {

	public static void main(String[] args) throws Exception {
		/**获取当前操作系统的换行符*/
		String lineSeparator = System.getProperty("line.separator");
		/**考虑到不同操作系统换行符的不同,Windows下是"\r\n",Linux下是"\n"(Unix下好像是"\r",不太确定)*/
		FileManager fma = new FileManager("a.txt", new String[]{"\n","\r\n"});
		/**"\\s"表示空格*/
		FileManager fmb = new FileManager("b.txt", new String[]{"\n","\r\n","\\s"});
		//c.txt文件保存位置是类路径即bin目录下
		FileWriter fw = new FileWriter(new File(ClassLoader.getSystemResource("").getFile(), "c.txt"));
		String aWord = null;
		String bWord = null;
		while((aWord=fma.nextWord())!=null) {
                         //添加“A:”是为了观察容易结果是按怎样的顺序写入文件的
                         fw.write("A:" + aWord + lineSeparator);
			if((bWord=fmb.nextWord())!=null) {
				fw.write("B:" +bWord+ lineSeparator);
			}
		}
		while((bWord=fmb.nextWord())!=null) {
			fw.write("B:" +bWord+ lineSeparator);
		}
		fw.flush();
		fw.close();
		/**关闭文件字符流对a.txt和b.txt资源的连接*/
		fma.close();
		fmb.close();
	}

}

class FileManager {
	String[] words = null;
	int pos = 0;
	FileInputStream fis = null;
	
	public FileManager() {
	}

	public FileManager(String fileName, String[] seperators) throws Exception {
		/**获取当前项目的类路径*/
		URL classpath = FileManager.class.getClassLoader().getResource("");

		File file = new File(classpath.getFile(), fileName);
		fis = new FileInputStream(file);

		/**从文件中读取数据*/
		byte[] a = new byte[fis.available()];
		int len = fis.read(a);
		int total = 0;
		//如果total小于字节数组的长度,说明文件没有读取完成
		while (total < a.length) {
			total += len;
			//向字节数组中写入数据,起始偏移量是已读取数据长度, a.length - total表示剩余数据长度
			len = fis.read(a, total, a.length - total);
		}
		StringBuffer regex = new StringBuffer();
		for(String seperator : seperators){
			regex.append(seperator + "|");
		}
		regex.deleteCharAt(regex.length()-1);
		words = new String(a).split(regex.toString());
	}
	
	public String nextWord(){
		if(pos >= words.length) {
			return null;
		}
		return words[pos++];
	}
	
	public void close() {
		if(fis!=null) {
			try {
				fis.close();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				fis = null;
			}
		}
	}
}

结果示例

从左到右文件名依次为:b.txt, a.txt, c.txt(c.txt并未显示完结果)

面试题之——java交织读取两个文件中单词,然后写入新的文件面试题之——java交织读取两个文件中单词,然后写入新的文件面试题之——java交织读取两个文件中单词,然后写入新的文件