对文本文件中的行子组进行排序
Sort subgroups of lines in text file
我有一个名为 filelist.txt
的服务器 属性 文件。此文件列表包含文件列表,这些文件根据目录前缀分组排列。内容的顺序很重要,因为文件必须以特定的文件名开始和结束,并且第一个子组必须首先出现。例如,文件如下所示:
config.txt
../../linux/a.txt
../../linux/c.txt
../../linux/d.txt
../../linux/b.txt
../../certificates/../../d.txt
../../certificates/../../a.txt
../../certificates/../../c.txt
../../certificates/../..b.txt
../../bin/b.txt
../../bin/a.txt
properties.server
我想知道的是,以干净高效的方式在这些子组中进行排序,同时保持子组的整体顺序的最佳方法是什么?
我写了这段代码,它可以按子组过滤并对其进行排序:
try(Stream<String> lines = Files.lines(Paths.get("src/filelist.txt"))){
List<String> linez = lines.filter(l -> l.contains("linux")).sorted().collect(Collectors.toList());
BufferedWriter bw = new BufferedWriter(new FileWriter("src/output.txt"));
for(String line : linez){
bw.write(line+"\n");
}
bw.close();
我可以有一个 List<String>
来包含我所有的行,我可以过滤原始文件行,对它们进行排序,然后将它们添加到这个列表中。
有几件事我不喜欢:
- 我不是在覆盖原始文件,而是在写入一个新文件。我想看看有没有办法覆盖而不是写入新文件。
- 好像有点迟钝。如果添加了新的目录前缀怎么办?然后我将不得不再次编辑此代码以过滤和排序新的目录前缀组。此外,根据每个过滤器为每个子组制作一堆不同的列表,而不是像
lines.filter(...).sort().filter(...).sort().filter(...)
这样的事情,会感觉很奇怪,但我认为这种语法还没有意义,因为一旦过滤器是已应用,排序后我无法取消应用过滤器。如果能够过滤、就地排序、然后应用另一个过滤器并排序等等,那将是非常好的。
我有哪些选择?
流不是最好的方法(至少不是对整个工作;也许对部分工作)。下面使用查看每一行的方法,如果到最后一个组件的路径与之前读取的相同,则将其添加到列表中,如果不相同,则对该路径列表进行排序,然后将它们添加到排序后的结果,使得每个前缀组的顺序不变,但每个组最终排序。
覆盖输入文件只是使用同一个文件进行读取和写入的问题 - 只需确保在打开它进行写入之前读取所有内容即可。下面只是在处理文件之前将文件读入列表。
import java.util.ArrayDeque;
import java.nio.file.Path;
import java.nio.file.Files;
import java.io.IOException;
import java.io.PrintWriter;
public class Demo {
public static void main(String[] args) {
try {
Path datafile = Path.of(args[0]);
var lines = Files.readAllLines(datafile);
var sorted = new ArrayDeque<String>(lines.size());
// Add first line
sorted.addLast(lines.get(0));
// Iterate through all the remaining but the last line
var block = new ArrayDeque<Path>();
for (int i = 1; i < lines.size() - 1; i++) {
Path p = Path.of(lines.get(i));
if (!block.isEmpty()
&& !p.getParent().equals(block.getLast().getParent())) {
// Current path has a different prefix than the current
// block. Sort block and add it to output
block.stream()
.sorted()
.forEachOrdered(sp ->
sorted.addLast(sp.toString()));
// And reset for a new path prefix
block.clear();
}
block.addLast(p);
}
// Handle the last block of paths
block.stream()
.sorted()
.forEachOrdered(sp -> sorted.addLast(sp.toString()));
// Add last line
sorted.addLast(lines.get(lines.size() - 1));
// Overwrite the original input file
Files.write(datafile, sorted);
} catch (IOException e) {
System.err.println(e);
System.exit(1);
}
}
}
这基于,但通过直接在原始列表中对受影响的组进行排序来简化操作。
try {
Path datafile = Path.of(args[0]);
var lines = Files.readAllLines(datafile);
// ensure that the list is mutable
if(lines.getClass() != ArrayList.class) lines = new ArrayList<>(lines);
int first = 1; // skip first line
int last = lines.size() - 1; // and last line
if(first >= last) return;
Path previous = Path.of(lines.get(first));
for (int i = first + 1; i < last; i++) {
Path p = Path.of(lines.get(i));
if(!p.getParent().equals(previous.getParent())) {
lines.subList(first, i).sort(null);
first = i;
previous = p;
}
}
// Handle the last block of paths
if(first < last) lines.subList(first, last).sort(null);
// Overwrite the original input file
Files.write(datafile, lines);
} catch (IOException e) {
System.err.println(e);
System.exit(1);
}
请注意,Files.lines
返回的列表类型未指定,但实际上总是 ArrayList
。该解决方案保护自己免受返回列表不可变的假设情况的影响,但在现实生活中不执行复制操作。所以它既形式上正确又有效。
我有一个名为 filelist.txt
的服务器 属性 文件。此文件列表包含文件列表,这些文件根据目录前缀分组排列。内容的顺序很重要,因为文件必须以特定的文件名开始和结束,并且第一个子组必须首先出现。例如,文件如下所示:
config.txt
../../linux/a.txt
../../linux/c.txt
../../linux/d.txt
../../linux/b.txt
../../certificates/../../d.txt
../../certificates/../../a.txt
../../certificates/../../c.txt
../../certificates/../..b.txt
../../bin/b.txt
../../bin/a.txt
properties.server
我想知道的是,以干净高效的方式在这些子组中进行排序,同时保持子组的整体顺序的最佳方法是什么?
我写了这段代码,它可以按子组过滤并对其进行排序:
try(Stream<String> lines = Files.lines(Paths.get("src/filelist.txt"))){
List<String> linez = lines.filter(l -> l.contains("linux")).sorted().collect(Collectors.toList());
BufferedWriter bw = new BufferedWriter(new FileWriter("src/output.txt"));
for(String line : linez){
bw.write(line+"\n");
}
bw.close();
我可以有一个 List<String>
来包含我所有的行,我可以过滤原始文件行,对它们进行排序,然后将它们添加到这个列表中。
有几件事我不喜欢:
- 我不是在覆盖原始文件,而是在写入一个新文件。我想看看有没有办法覆盖而不是写入新文件。
- 好像有点迟钝。如果添加了新的目录前缀怎么办?然后我将不得不再次编辑此代码以过滤和排序新的目录前缀组。此外,根据每个过滤器为每个子组制作一堆不同的列表,而不是像
lines.filter(...).sort().filter(...).sort().filter(...)
这样的事情,会感觉很奇怪,但我认为这种语法还没有意义,因为一旦过滤器是已应用,排序后我无法取消应用过滤器。如果能够过滤、就地排序、然后应用另一个过滤器并排序等等,那将是非常好的。
我有哪些选择?
流不是最好的方法(至少不是对整个工作;也许对部分工作)。下面使用查看每一行的方法,如果到最后一个组件的路径与之前读取的相同,则将其添加到列表中,如果不相同,则对该路径列表进行排序,然后将它们添加到排序后的结果,使得每个前缀组的顺序不变,但每个组最终排序。
覆盖输入文件只是使用同一个文件进行读取和写入的问题 - 只需确保在打开它进行写入之前读取所有内容即可。下面只是在处理文件之前将文件读入列表。
import java.util.ArrayDeque;
import java.nio.file.Path;
import java.nio.file.Files;
import java.io.IOException;
import java.io.PrintWriter;
public class Demo {
public static void main(String[] args) {
try {
Path datafile = Path.of(args[0]);
var lines = Files.readAllLines(datafile);
var sorted = new ArrayDeque<String>(lines.size());
// Add first line
sorted.addLast(lines.get(0));
// Iterate through all the remaining but the last line
var block = new ArrayDeque<Path>();
for (int i = 1; i < lines.size() - 1; i++) {
Path p = Path.of(lines.get(i));
if (!block.isEmpty()
&& !p.getParent().equals(block.getLast().getParent())) {
// Current path has a different prefix than the current
// block. Sort block and add it to output
block.stream()
.sorted()
.forEachOrdered(sp ->
sorted.addLast(sp.toString()));
// And reset for a new path prefix
block.clear();
}
block.addLast(p);
}
// Handle the last block of paths
block.stream()
.sorted()
.forEachOrdered(sp -> sorted.addLast(sp.toString()));
// Add last line
sorted.addLast(lines.get(lines.size() - 1));
// Overwrite the original input file
Files.write(datafile, sorted);
} catch (IOException e) {
System.err.println(e);
System.exit(1);
}
}
}
这基于
try {
Path datafile = Path.of(args[0]);
var lines = Files.readAllLines(datafile);
// ensure that the list is mutable
if(lines.getClass() != ArrayList.class) lines = new ArrayList<>(lines);
int first = 1; // skip first line
int last = lines.size() - 1; // and last line
if(first >= last) return;
Path previous = Path.of(lines.get(first));
for (int i = first + 1; i < last; i++) {
Path p = Path.of(lines.get(i));
if(!p.getParent().equals(previous.getParent())) {
lines.subList(first, i).sort(null);
first = i;
previous = p;
}
}
// Handle the last block of paths
if(first < last) lines.subList(first, last).sort(null);
// Overwrite the original input file
Files.write(datafile, lines);
} catch (IOException e) {
System.err.println(e);
System.exit(1);
}
请注意,Files.lines
返回的列表类型未指定,但实际上总是 ArrayList
。该解决方案保护自己免受返回列表不可变的假设情况的影响,但在现实生活中不执行复制操作。所以它既形式上正确又有效。