如何优雅地读取文件中的每一行?

最近文件操作较多,但大多数都是一行一行地读取,每一行是一条新闻。经常用的代码是这样的:

InputStream is = null;
try {
    is = new FileInputStream(textPath);
    BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"), 512);
    // 读取一行,存储于字符串列表中
    for (String line = reader.readLine(); line != null; line = reader.readLine()) {
        line = line.trim();
        // do something here
    }

}catch (FileNotFoundException fnfe){
    fnfe.printStackTrace();
}catch (IOException ioe){
    ioe.printStackTrace();
} finally {
    try {
        if (is != null) {
            is.close();
            is = null;
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

当do something变得很庞大时,这try语句块就变得有点臃肿。是否能存在这样的一个文件读取类,就像Iterator迭代器一样,使用hasNext()和next()遍历文件中的所有行,而将异常处理等全部隐藏起来?还是有什么其它更加优雅的方法?

阅读 37.5k
6 个回答

java当然可以很优雅, 宇宙第一的编程语言也不是光靠sun和oracle忽悠出来的.

这里 我们需要把 文件处理 的逻辑 和 业务逻辑分开, 引入 strategy 模式是比较合适的. 文件处理的部分是不变的可以重用的, 业务逻辑是变化的.

还有就是java7 引入 try-with-resource, 已经不需要自己去关闭流了.

代码:

public interface FileProcessor {
    void processByLine(String FilePath, LineProcessor processor);
}

public interface LineProcessor {
    void doSomeThing(String oneLine);
}

public class FileProcessorImpl implements FileProcessor {

    @Override
    public void processByLine(String filePath, LineProcessor processor) {
        try (BufferedReader br =
                new BufferedReader(new FileReader(filePath))) {
                String line = br.readLine();
                processor.doSomeThing(line.trim());
        }catch (FileNotFoundException fnfe){
            fnfe.printStackTrace();
        }catch (IOException ioe){
            ioe.printStackTrace();
        }
    }
}

public class FileProcessImplTest {

    @Test
    public void testProcessByLine() {
        final StringBuilder sb=new StringBuilder();
        final StringBuilder sb1=new StringBuilder();
        FileProcessor fp=new FileProcessorImpl();

        fp.processByLine("a.txt", new LineProcessor(){
            @Override
            public void doSomeThing(String oneLine) {
                sb.append(oneLine);
            }
        });

        fp.processByLine("a_reverse_by_line.txt", new LineProcessor(){
            @Override
            public void doSomeThing(String oneLine) {
                sb1.append(new StringBuilder(oneLine).reverse());
            }
        });

        assertEquals(sb.toString(), sb1.toString());
    }

}

可以使用 commons-ioFileUtils:

LineIterator:

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.LineIterator;

final LineIterator it = FileUtils.lineIterator(new File(""), "UTF-8");
try {
    while (it.hasNext()) {
        final String line = it.nextLine();
    }
} finally {
    it.close();
}

如果内存足够:

import org.apache.commons.io.Charsets;
import org.apache.commons.io.FileUtils;

final List<String> lines = FileUtils.readLines(new File(""), Charsets.UTF_8);

另外推荐 LZ 使用 org.apache.commons.io.Charsets.UTF_8 代替字符串的 "UTF-8",如:

// 不会抛出 UnsupportedEncodingException
new InputStreamReader(is, Charsets.UTF_8)
str.getBytes(Charsets.UTF_8);

因为 Java 规定了所有平台都必须实现 UTF-8 ,这里本来就不可能抛出 UnsupportedEncodingException 。Java 7 中我们就可以用 java.nio.charset.StandardCharsets 了,Java 6 暂时拿这个顶一下。

另外 LZ 可以用 org.apache.commons.io.IOUtils.closeQuietly 来关闭一个流。你的 finally 块可以简化为:

finnaly {
    IOUtils.closeQuiety(is);
}

" 每一个优雅的接口背后,都有一个龌龊的实现 “

用python可好?

for line in open(textPath,'r'):
    print line

撸主,「瓜娃」guava 你值得拥有:

import java.io.File;
import java.io.IOException;

import com.google.common.base.Charsets;
import com.google.common.io.Files;

public class App {
    public static void main(String[] args) throws IOException {
        for (String line : Files.readLines(new File("~/test.txt"), Charsets.UTF_8)) {
            System.out.println(line);
        }
    }
}

ps:看了下源码,是把整个文件读入到 List<String>,大文件会把内存撑爆,可以自己山寨一个:

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Iterator;

public class Test {
    public static void main(String[] args) throws FileNotFoundException {
        for (String line : IO.lineSeq("d:/test.txt")) {
            System.out.println(line);
        }
    }
}

final class IO {
    public static Iterable<String> lineSeq(final String path) throws FileNotFoundException {

        return new Iterable<String>() {
            BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(
                                      path)));

            public Iterator<String> iterator() {
                return new Iterator<String>() {
                    private String content;

                    public void remove() {
                        throw new UnsupportedOperationException();
                    }

                    public String next() {
                        return content;
                    }

                    public boolean hasNext() {
                        try {
                            content = reader.readLine();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }

                        if (content != null) {
                            return true;
                        }

                        try {
                            reader.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }

                        return false;
                    }
                };
            }

        };

    }
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏