使用 Files.lines 修改文件文件、Files、lines

2023-09-07 01:11:55 作者:你是如此的自作多情 #

我想读入一个文件并用新文本替换一些文本.使用 asm 和 int 21h 会很简单,但我想使用新的 java 8 流.

I'd like to read in a file and replace some text with new text. It would be simple using asm and int 21h but I want to use the new java 8 streams.

    Files.write(outf.toPath(), 
        (Iterable<String>)Files.lines(inf)::iterator,
        CREATE, WRITE, TRUNCATE_EXISTING);

我想要一个 lines.replace("/*replace me*/","new Code() ");.新行是因为我想测试在某处插入一段代码.

Somewhere in there I'd like a lines.replace("/*replace me*/","new Code() ");. The new lines are because I want to test inserting a block of code somewhere.

这是一个播放示例,它不能按我的意愿工作,但可以编译.我只需要一种方法来截取迭代器中的行,并用代码块替换某些短语.

Here's a play example, that doesn't work how I want it to, but compiles. I just need a way to intercept the lines from the iterator, and replace certain phrases with code blocks.

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import static java.nio.file.StandardOpenOption.*;
import java.util.Arrays;
import java.util.stream.Stream;

public class FileStreamTest {

    public static void main(String[] args) {
        String[] ss = new String[]{"hi","pls","help","me"};
        Stream<String> stream = Arrays.stream(ss);

        try {
            Files.write(Paths.get("tmp.txt"),
                    (Iterable<String>)stream::iterator,
                    CREATE, WRITE, TRUNCATE_EXISTING);
        } catch (IOException ex) {}

//// I'd like to hook this next part into Files.write part./////
        //reset stream
        stream = Arrays.stream(ss);
        Iterable<String> it = stream::iterator;
        //I'd like to replace some text before writing to the file
        for (String s : it){
            System.out.println(s.replace("me", "my
real
name"));
        }
    }

}

我已经做到了这一点并且它有效.我正在尝试使用过滤器,也许它不是真的必要.

edit: I've gotten this far and it works. I was trying with filter and maybe it isn't really necessary.

        Files.write(Paths.get("tmp.txt"),
                 (Iterable<String>)(stream.map((s) -> {
                    return s.replace("me", "my
real
name");
                }))::iterator,
                CREATE, WRITE, TRUNCATE_EXISTING);

推荐答案

Files.write(..., Iterable, ...) 方法在这里看起来很诱人,但是将 Stream 转换为可迭代使这很麻烦.它还从 Iterable 中拉取",这有点奇怪.如果文件写入方法可以在 forEach 之类的东西中用作流的终端操作,那将更有意义.

The Files.write(..., Iterable, ...) method seems tempting here, but converting the Stream to an Iterable makes this cumbersome. It also "pulls" from the Iterable, which is a bit odd. It would make more sense if the file-writing method could be used as the stream's terminal operation, within something like forEach.

不幸的是,大多数编写的东西都会抛出 IOException,这是 forEach 所期望的 Consumer 功能接口所不允许的.但 PrintWriter 不同.至少,它的写法不会抛出检查异常,虽然打开还是会抛出IOException.以下是它的使用方法.

Unfortunately, most things that write throw IOException, which isn't permitted by the Consumer functional interface that forEach expects. But PrintWriter is different. At least, its writing methods don't throw checked exceptions, although opening one can still throw IOException. Here's how it could be used.

Stream<String> stream = ... ;
try (PrintWriter pw = new PrintWriter("output.txt", "UTF-8")) {
    stream.map(s -> s.replaceAll("foo", "bar"))
          .forEachOrdered(pw::println);
}

注意 forEachOrdered 的使用,它以与读取输出行相同的顺序打印输出行,这大概是您想要的!

Note the use of forEachOrdered, which prints the output lines in the same order in which they were read, which is presumably what you want!

如果您正在从输入文件中读取行,修改它们,然后将它们写入输出文件,则将两个文件放在同一个 try-with-resources 语句中是合理的:

If you're reading lines from an input file, modifying them, and then writing them to an output file, it would be reasonable to put both files within the same try-with-resources statement:

try (Stream<String> input = Files.lines(Paths.get("input.txt"));
     PrintWriter output = new PrintWriter("output.txt", "UTF-8"))
{
    input.map(s -> s.replaceAll("foo", "bar"))
         .forEachOrdered(output::println);
}