java - 在文本文件中查找单词并使用 Java 将它们保存到新文件中
问题描述
我正在尝试编写一个程序来查找 txt 文件中的特定单词,并将整行写入单独的文件中。现在我的程序只是重写整个文件并将其保存到新文件中。这部分对于将替换这些行的程序的其余部分是必需的,但我已经创建了。我只是Java的初学者,我无法处理它。我需要使用正则表达式吗?
import java.io.*;
public class change {
public static void main(String[] args) {
try {
BufferedWriter bw = new BufferedWriter(
new FileWriter("path\\file.txt"));
BufferedReader br = new BufferedReader(
new FileReader("path\\newfile.txt"));
String s;
while((s = br.readLine()) != null){
bw.write(s + "\n");
}
br.close();
bw.close();
} catch(Exception ex){
return;
}
}
}
解决方案
此代码存在多个问题。
可悲的异常处理
如果出现问题,此代码将获取有关您的异常的所有有用信息(至少有 4 件事:异常的类型、异常的消息、堆栈跟踪和因果链),并将其扔进垃圾箱。
你会写错误。大量的 em。您对应用程序运行位置的假设将改变您。
例外对于管理这一点至关重要。
不要再写这样的代码了。修复很简单,甚至可以减少和简化您的代码!简单地堆throws Exception
在你的main
声明(public static void main(String[] args) throws Exception {
)的末尾,并删除 try、catch 和 catch 中的 return。
这样,异常就会冒泡到任何调用main
自身的地方,并且会通过打印所有各种有用的部分并退出您的应用程序来“处理”它。这就是你想要的。
字符集违规
文件是字节包,但Writer
以Reader
字符为单位。每当字节转换为字符或反之亦然时,都会应用编码。UTF-8 是一种编码。ISO-8859-1、MacRoman、CP452 等也是如此。
如果您没有看到正在应用的编码,那么通常这意味着应用了“平台默认值”,而您几乎从不想要这样。它导致代码似乎可以正常工作,直到您在其他地方或在不同的文件上运行它,然后一切都崩溃了。
解决方法是使用已融入其规范且默认为 UTF_8(例如新Files
API)的 API,或显式指定。顺便说一句,不幸的是FileWriter
,FileReader
不适合目的;永远不要使用这些类,它们按设计有效地被破坏了。他们的 API 也已过时,有更新的 API。
使用旧的 API,您可以按如下方式修复它:
new BufferedWriter(new InputStreamWriter(new FileInputStream("path\\file.txt"), StandardCharsets.UTF_8));
使用新的 API 会涉及更多,见下文。
资源管理违规
当您打开资源时,您也必须明确地关闭它们。Java 是一种垃圾收集语言,但垃圾收集的“要点”是 JVM 会在它认为有必要时运行收集,而且可能需要几天时间。因此,如果您将其放置在任何地方,则任何占用的不仅仅是内存的对象都需要显式关闭。文件的“句柄”就是这样一种资源:您的操作系统提供的这些资源有限,并且一旦打开一定数量的进程,就会完全拒绝让进程打开更多文件。您的代码打开文件但可能不再关闭它们:如果发生异常,则永远不会执行该br.close()
行bw.close()
。
Java为此提供了一个工具:自动资源管理(ARM),也称为“try-with-resources”。替换这个:
BufferedWriter bw = ....;
// do stuff with the writer
bw.close();
和:
try (BufferedWriter bw = ....) {
// do stuff with the writer
}
您使用 writer的{}
标记:Java 确保无论您如何“退出”这些{}
资源,资源都将被关闭。无论您只是到达此块的末尾,还是返回/中断/继续退出,或者异常导致您退出,资源都已关闭。
搜索字符串
regexps 是一种方法,但如果您正在寻找一个特定的确切字符串,那就太过分了。字符串有一个.contains()
方法。
新 API
有一个新的 API。旧的 File API 有一种讨厌的倾向,即false
当事情无法完成时才返回,而不是像“普通”java API 那样告诉你它。它还具有更大的灵活性:它可以处理诸如管理文件所有权、文件创建标记、访问控制和链接之类的事情。它还有一个很好的特性,即默认字符集编码(如果您不指定任何内容)是 UTF-8,而不是可怕且极其无用的“平台默认值”。
反斜杠
没有必要,即使在窗户上也是如此。path/file.txt
也可以。它更容易阅读并且更常见。
var
您可以使用var
而不是第二次写出可以派上用场的类型。var x = new Foo();
是一样的Foo x = new Foo()
。
把它们放在一起
public class Change {
public static void main(String[] args) throws Exception {
try (
var bw = Files.newBufferedWriter(Paths.get("path/newfile.txt"));
var br = Files.newBufferedReader(Paths.get("path/file.txt"));
) {
String s;
while ((s = br.readLine()) != null) {
if (s.contains("StringToFind")) {
// or: s.matches("^.*String[a-zA-Z]WithRegexpToFind.*$")
bw.write(s + "\n");
}
}
}
}
}
推荐阅读
- symfony - 如何防止 Symfony 4 中出现“类名或命名空间可能有错字”的错误?
- php - 提交到另一个页面后通过 id 从数据库中获取数据
- python - 如何在 python 中使用 sys.stdin.read()
- python - 绘制小平面网格的图例
- foreach - 对于使用 Visual Studio 2017 在 C++ CLI 中来自 MSDN 的每个都会产生编译错误
- html - 检查与页面加载不同的元素悬停行为
- hl7 - SIU23 阻断时间
- javascript - Array.prototype.sort() 返回错误的顺序
- c# - ASP.NET Core 2.1 Web.API 更改应用程序洞察力记录的检测密钥
- nginx - 后端服务器响应后更改主机头