查找和替换文件中的单词/行
Posted
技术标签:
【中文标题】查找和替换文件中的单词/行【英文标题】:Find and replace words/lines in a file 【发布时间】:2011-04-25 13:09:02 【问题描述】:我有一个文件(更具体地说,是一个 log4j 配置文件),我希望能够读取该文件并挑选出代码中的某些行并替换它们。例如,在文件中有一个文本字符串,指示它存储的目录或记录器的级别。我希望能够在不读取文件、将其写入另一个文件并删除原始文件的情况下替换这些文本字符串。有没有更有效的方法来使用 Java 查找和替换文件中的文本?
这是我正在尝试使用的文本文件的示例:
log4j.rootLogger=DEBUG, A0
log4j.appender.A0=org.apache.log4j.RollingFileAppender
log4j.appender.A0.File=C:/log.txt
log4j.appender.A0.MaxFileSize=100KB
log4j.appender.A0.MaxBackupIndex=1
log4j.appender.A0.layout=org.apache.log4j.RollingFileAppender
log4j.appender.A0.layout.ConversionPattern=%-4r [%t] %-5p: %c %x - %m%n
我希望能够读取文件并将“DEBUG”替换为另一个级别或替换文件目录名称“C:/log.txt”。日志配置文件也是用xml写的。下面是一个例子。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration>
<appender class="org.apache.log4j.RollingFileAppender" name="A0">
<param name="append" value="false"/>
<param name="File" value="C:/log/.txt"/>
<param name="MaxBackupIndex" value="1"/>
<param name="MaxFileSize" value="100KB"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-4r [%t] %-5p: %c %x - %m%n"/>
</layout>
</appender>
<root>
<level value="DEBUG"/>
<appender-ref ref="A0"/>
</root>
</log4j:configuration>
我在想这种类型的实现可以使用哈希映射吗?
【问题讨论】:
【参考方案1】:您可能希望使用 Scanner 解析并找到您要修改的特定部分。还有可以使用的 Split 和 StringTokenizer,但在您在 Scanner 工作的级别上可能是需要的。
以下是有关它们之间区别的一些附加信息: Scanner vs. StringTokenizer vs. String.Split
【讨论】:
【参考方案2】:您可以使用 Java 的 Scanner
类解析文件的单词并在应用程序中处理它们,然后使用 BufferedWriter
或 FileWriter
写回文件,应用更改。
我认为有一种更有效的方法可以在某些时候获取扫描仪的迭代器位置,以便更好地实现编辑。但是由于文件要么打开以供读取或写入,因此我不确定。
在任何情况下,您都可以使用已经可用于解析 XML 文件的库,这些库已经实现了所有这些,并且可以让您轻松地做您想做的事情。
【讨论】:
【参考方案3】:任何体面的文本编辑器都具有支持正则表达式的搜索和替换功能。
但是,如果您有理由在 Java 中重新发明***,您可以这样做:
Path path = Paths.get("test.txt");
Charset charset = StandardCharsets.UTF_8;
String content = new String(Files.readAllBytes(path), charset);
content = content.replaceAll("foo", "bar");
Files.write(path, content.getBytes(charset));
这仅适用于 Java 7 或更高版本。如果您卡在较旧的 Java 上,您可以这样做:
String content = IOUtils.toString(new FileInputStream(myfile), myencoding);
content = content.replaceAll(myPattern, myReplacement);
IOUtils.write(content, new FileOutputStream(myfile), myencoding);
在这种情况下,您需要添加错误处理并在完成后关闭流。
IOUtils
记录在 http://commons.apache.org/proper/commons-io/javadocs/api-release/org/apache/commons/io/IOUtils.html
【讨论】:
关闭 FileInputStream 和 FileOutputStream 怎么样? 留给读者作为练习(参见代码示例后面的句子)。 您正在将整个文件加载到内存中,在这种情况下这可能是安全的。但是更大的文件(例如 2 GB)呢?我认为困难(但正确的做法)是将文件从源流传输到目标。 我不明白这个问题第一部分的态度。如果有人想在程序或集成测试中修改文件怎么办。这是如何重新发明***的? 这对自动化非常有用,不应该被忽视。【参考方案4】:这是我通常会使用脚本语言来处理的事情。能够非常简单地使用 Ruby/Perl/Python(在此处插入您最喜欢的脚本语言)之类的工具来执行这些类型的转换是非常有用的。
我通常不会为此使用 Java,因为它在开发周期/打字等方面太重了。
请注意,如果您想特别处理 XML,建议将文件作为 XML 读取并按原样进行操作(上述脚本语言具有非常有用且简单的 API 可用于执行此类工作)。一个简单的文本搜索/替换可以使您的文件在字符编码等方面无效。与往常一样,这取决于您的搜索/替换要求的复杂性。
【讨论】:
【参考方案5】:public static void replaceFileString(String old, String new) throws IOException
String fileName = Settings.getValue("fileDirectory");
FileInputStream fis = new FileInputStream(fileName);
String content = IOUtils.toString(fis, Charset.defaultCharset());
content = content.replaceAll(old, new);
FileOutputStream fos = new FileOutputStream(fileName);
IOUtils.write(content, new FileOutputStream(fileName), Charset.defaultCharset());
fis.close();
fos.close();
以上是对我有用的 Meriton 示例的实现。文件名是目录(即 D:\utilities\settings.txt)。我不确定应该使用什么字符集,但我刚才在 Windows XP 机器上运行了这段代码,它在没有创建临时文件和重命名的情况下就成功了。
【讨论】:
顺便说一句,我使用“org.apache.commons.configuration.PropertiesConfiguration”作为设置类,它只是一个键/值属性文件【参考方案6】:在访问了这个问题并注意到所选解决方案的最初担忧之后,我想我会为那些不使用 Java 7 的人贡献这个问题,Java 7 使用 FileUtils 而不是来自 Apache Commons 的 IOUtils。这里的优点是readFileToString 和writeStringToFile 自动为您处理关闭文件的问题。 (writeStringToFile 没有记录它,但您可以阅读源代码)。希望这个秘诀可以为刚遇到这个问题的人简化事情。
try
String content = FileUtils.readFileToString(new File("InputFile"), "UTF-8");
content = content.replaceAll("toReplace", "replacementString");
File tempFile = new File("OutputFile");
FileUtils.writeStringToFile(tempFile, content, "UTF-8");
catch (IOException e)
//Simple exception handling, replace with what's necessary for your use case!
throw new RuntimeException("Generating file failed", e);
【讨论】:
不需要replaceAll,使用replace即可。第一个使用正则表达式。第二个只是替换所有找到的字符串内容以上是关于查找和替换文件中的单词/行的主要内容,如果未能解决你的问题,请参考以下文章