如何在 Java 中拆分行尾?

How do you split at the end of the line in Java?

提问人:4K4K1N 提问时间:8/28/2023 最后编辑:4K4K1N 更新时间:8/28/2023 访问量:106

问:

我有一个 .txt 文件,如下所示:

Event=ThermostatNight,time=0
Event=LightOn,time=2000
Event=WaterOff,time=10000
Event=ThermostatDay,time=12000
Event=Bell,time=9000,rings=5
Event=WaterOn,time=6000
Event=LightOff,time=4000
Event=Terminate,time=20000
Event=FansOn,time=7000
Event=FansOff,time=8000

我正在尝试将上述内容存储到一个数组中,例如数组应该是:

[0] = Event
[1] = ThermostatNight
[2] = time
[3] = 0
[4] = Event
[5] = LightOn
[6] = time
[7] = 2000
//.... etc

基本上,我试图将事件名称存储为变量,将时间存储为另一个变量,并使用 = 和 作为拆分点。但是,索引 [3] 显示“0Event”,索引 [6] 显示“2000Event”,依此类推。

我不确定我是否应该使用新行作为拆分点或空白,或者我可能只是以错误的方式看待这个问题。这是我的代码:

public static void main(String[] 参数) {

try {
    
Scanner content = new Scanner(new File("file.txt")).useDelimiter(",");

StringBuilder sb = new StringBuilder();
    
while(content.hasNext()) {
    sb.append(content.nextLine());
        }

String[] wordsArray = sb.toString().split("[=,]");

//Test what is at each index
System.out.println(wordsArray[3]);          
}
    
catch (FileNotFoundException e) {
    e.printStackTrace();
}

}

我一直在玩 String[] wordsArray = sb.toString().split(“[=,]”);并尝试将代码修改为:

.split("\[=,\\n\]")
.split("\[=,\\s\]")
.split("\[=,\\n\]")
...etc

以及 .split 中的许多其他不同参数。

但我仍然不断将索引 [3] 和 [6] 作为 0Event 和 2000Event。

再次不确定我是否只是以错误的方式看待这个问题或采取错误的方法。任何帮助将不胜感激。

Java 数组 文件 分隔符

评论

2赞 Jhilton 8/28/2023
当您读取换行符时,换行符正在被剥离。因此您的 StringBuilder 没有用于 的换行符。将它附加到每一行的 StringBuilder 中,然后您可以在操作中使用该模式,例如(请参阅 docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html 中的换行符匹配器content.nextLine()String.split\\Rsplit.split("\[=,\\R]"))
0赞 tgdavies 8/28/2023
在调试器下运行程序可以让你看到你期望的换行符没有换行符。

答:

3赞 Basil Bourque 8/28/2023 #1

TL的;博士

我发现多次拆分而不是正则表达式更简单、更清晰、更自我记录。

input
        .lines ( )
        .flatMap ( line -> Arrays.stream ( line.split ( "," ) ) )
        .flatMap ( pair -> Arrays.stream ( pair.split ( "=" ) ) )
        .toArray ( String[] :: new );

拆分 3 次,嵌套

为什么不使用三重嵌套调用来拆分?

在外循环中,按线拆分。在逗号上的拆分中。第三,在 EQUALS SIGN 上拆分。

String input = """
        Event=ThermostatNight,time=0
        Event=LightOn,time=2000
        Event=WaterOff,time=10000
        Event=ThermostatDay,time=12000
        Event=Bell,time=9000,rings=5
        Event=WaterOn,time=6000
        Event=LightOff,time=4000
        Event=Terminate,time=20000
        Event=FansOn,time=7000
        Event=FansOff,time=8000
        """;
List < String > list = new ArrayList <> ( );
for ( String line : input.lines ( ).toList ( ) )
{
    for ( String pair : line.split ( "," ) )
    {
        for ( String keyOrValue : pair.split ( "=" ) )
        {
            list.add ( keyOrValue );
        }
    }
}
String[] array = list.toArray ( String[] :: new );

array = [Event, ThermostatNight, time, 0, Event, LightOn, time, 2000, Event, WaterOff, time, 10000, Event, ThermostatDay, time, 12000, Event, Bell, time, 9000, rings, 5, Event, WaterOn, time, 6000, Event, LightOff, time, 4000, Event, Terminate, time, 20000, Event, FansOn, time, 7000, Event, FansOff, time, 8000]


您的 IDE 可能会建议将该内部循环替换为 调用 。addAll

for ( String line : input.lines ( ).toList ( ) )
{
    for ( String pair : line.split ( "," ) )
    {
        list.addAll ( Arrays.asList ( pair.split ( "=" ) ) );
    }
}

对于更紧凑的代码,我们可以使用流并生成单行代码。flatMap

String[] array =
        input
                .lines ( )
                .flatMap ( line -> Arrays.stream ( line.split ( "," ) ) )
                .flatMap ( pair -> Arrays.stream ( pair.split ( "=" ) ) )
                .toArray ( String[] :: new );

评论

1赞 z atef 8/28/2023
如果 OP 接受这一点或提供反馈,那将是正确的事情。
0赞 4K4K1N 8/28/2023
谢谢你,但我需要让它与我的文件一起工作,而不是将所有内容作为字符串。扫描程序内容 = 新扫描程序(新文件(“文件.txt”)).useDelimiter(“,”);
1赞 experiment unit 1998X 8/28/2023
@Kyle这只是一个例子。您应该参考答案以编写自己的答案
0赞 Basil Bourque 8/28/2023
@4K4K1N Files.lines 的工作方式与此处的答案中所示。两者都返回对象的 a。String#linesStreamString
1赞 Holger 8/28/2023
不需要执行三个嵌套拆分操作,因为结果与匹配所有三个分隔符的单个拆分操作没有什么不同:相同的逻辑也可以应用于String[] array = input.split("\\R|[,=]");ScannerString[] array = new Scanner(input) .useDelimiter("\\R|[,=]").tokens().toArray(String[]::new);
0赞 Yusuf Khan 8/28/2023 #2

您应该一次分隔一个步骤的数据,并一次解析一行,而不是一次全部解析。

因此,在您的情况下,每行 (content.nextLine) 都应该用逗号分隔:

String attributes = line.split(',');

然后,获取属性并单独拆分它们,以获取密钥和值:

for(String attribute : attributes) {
    String pairs = attribute.split('=');
    String key = attribute[0]; // Event, or time
    String value = attribute[1]; // LightsOn, or 2000

    // Do stuff with key and value, like add in your array
}

作为旁注:

除非您需要将其添加到原始数组中,否则我建议创建一个对象来表示您正在解析的数据。这样,您就可以将该数据的新对象(即 Event(String name, long time))添加到事件的 ArrayList 中。

0赞 Reilas 8/28/2023 #3

"...我正在尝试将上述内容存储到一个数组中......”

"...我不确定我是否应该使用新行作为分割点或空白,或者我可能只是以错误的方式看待这个问题......”

有几种方法可以完成此任务。

可以向 Scanner#useDelimiter 调用添加更多值,然后使用 next 而不是 nextLine

\r\n?|\n|[,=]

这将允许您摆脱 StringBuilder
对于 wordsArray,最初使用 List,并在需要时转换为数组。

try {

Scanner content = new Scanner(new File("file.txt")).useDelimiter("\r\n?|\n|[,=]");

List<String> list = new ArrayList<>();

while(content.hasNext()) {
    list.add(content.next());
        }

String[] wordsArray = list.toArray(new String[0]);

//Test what is at each index
System.out.println(wordsArray[3]);
}

catch (FileNotFoundException e) {
    e.printStackTrace();
}

或者,按原样使用扫描程序,不使用修改的分隔符,并使用 String#split 方法。

while(content.hasNextLine())
    wordsArray.addAll(List.of(content.nextLine().split("[,=]")));
try {

Scanner content = new Scanner(new File("file.txt"));

List<String> list = new ArrayList<>();

while(content.hasNext()) {
    list.addAll(List.of(content.nextLine().split("[,=]")));
        }

String[] wordsArray = list.toArray(new String[0]);

//Test what is at each index
System.out.println(wordsArray[3]);
}

catch (FileNotFoundException e) {
    e.printStackTrace();
}

输出

0

"...再次不确定我是否只是以错误的方式看待这个问题或采取错误的方法。任何帮助都非常感谢。

可以使用提供 lines 方法的 BufferedReader 类代替 Scanner

try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
    List<String> list = reader.lines()
                              .flatMap(x -> Stream.of(x.split("[,=]")))
                                                  .toList();
}

评论

1赞 4K4K1N 8/28/2023
这奏效了,谢谢!摆脱 StringBuilder 会有所帮助,并将正确的值添加到分隔符中就可以了。我测试了代码,它现在可以正常输出。
1赞 Holger 8/28/2023
请注意,有一种模式可以处理各种换行符,无需使用类似 的 。此外,从 Java 9 开始支持 Stream API,因此您可以简单地使用,例如 (必须以 A 必须关闭的方式关闭 A)\R\r\n?|\nScannerString[] array; try(Scanner s = new Scanner(new File("file.txt")) .useDelimiter("\\R|[,=]")) { array = s.tokens().toArray(String[]::new); }ScannerReader
0赞 Reilas 8/28/2023
@Holger,我听说有一些错误,它计算不正确。A 将与 A 匹配,这是不正确的;单个应与整个序列匹配。我从那里没有研究太多,所以我不推荐它。Scanner 流很好,我不怎么使用 Scanner。您应该将其添加为答案。\R\r\n\R\R\r\n\R
1赞 Holger 8/28/2023
好吧,评估为 ,考虑到工作方式,这是可以预料的。你的模式也会发生同样的情况,即 计算结果为 。但这并不影响方式,工作。换句话说,both 和 正确地生成一个包含两个字符串 , , 的数组,中间没有空字符串。"\r\n".matches("\\R\\R")truematches"\r\n".matches("(\r\n?|\n){2}")truesplit"X\r\nY".split("\\R")"X\r\nY".split("\r\n?|\n")"X""Y"
1赞 g00se 8/28/2023 #4

上面提到的人,你最好采用面向对象的方法来建模你的活动。请注意具有“rings”属性的异常记录。这将是描述重复的另一种方式。这里是铃铛,但可能是蜂鸣器或其他东西。 完成此操作后,可以将文件解析为对象:Event

public class Event {
    private String name;
    private int duration;
    private int numRepetitions;

    public Event() {
    }

    public Event(String name, int duration, int numRepetitions) {
        this.name = name;
        this.duration = duration;
        this.numRepetitions = numRepetitions;
    }

    public String getName() {
        return this.name;
    }

    public int getDuration() {
        return this.duration;
    }

    public int getNumRepetitions() {
        return this.numRepetitions;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setDuration(int duration) {
        this.duration = duration;
    }

    public void setNumRepetitions(int numRepetitions) {
        this.numRepetitions = numRepetitions;
    }

    public static Event fromCsv(String[] tokens) {
        if (tokens.length == 4) {
            return new Event(tokens[1], Integer.parseInt(tokens[3]), 0);
        } else {
            return new Event(tokens[1], Integer.parseInt(tokens[3]), Integer.parseInt(tokens[5]));
        }
    }

    public String toString() {
        return String.format("%s=%s,%s=%d,%s=%d", "name", name, "duration", duration, "numRepetitions", numRepetitions);
    }
}

解析文件:

import java.nio.file.Path;
import java.nio.file.Files;
import java.util.List;
import java.io.IOException;

public class EventParser {
    public static void main(String[] args) throws Exception {
        List<Event> events = EventParser.parseEvents(Path.of(args[0]));
        events.forEach(System.out::println);
    }

    public static List<Event> parseEvents(Path p) throws IOException {
        return Files.lines(p)
            .map(line ->line.split("[,=]"))
            .map(Event::fromCsv)
            .toList();
    }

}

给出输出:

name=ThermostatNight,duration=0,numRepetitions=0
name=LightOn,duration=2000,numRepetitions=0
name=WaterOff,duration=10000,numRepetitions=0
name=ThermostatDay,duration=12000,numRepetitions=0
name=Bell,duration=9000,numRepetitions=5
name=WaterOn,duration=6000,numRepetitions=0
name=LightOff,duration=4000,numRepetitions=0
name=Terminate,duration=20000,numRepetitions=0
name=FansOn,duration=7000,numRepetitions=0
name=FansOff,duration=8000,numRepetitions=0