使用缓冲区 reader 读取给定输入的数据块
Using a buffer reader to read blocks of data for a given input
这是我正在读取的文件的结构:
[MESSAGE BEGIN]
uan:123
messageID: 111
[MESSAGE END]
[MESSAGE BEGIN]
uan:123
status:test
[MESSAGE END]
我想要做的是,对于给定的 uan,return 它的所有细节,同时保持块结构“MESSAGE BEGIN”“MESSAGE END”。
这是我写的代码:
startPattern= "uan:123"
endPattern= "[MESSAGE END]"
System.out.println("Matching: " + this.getStartPattern());
List<String> desiredLines = new ArrayList<>();
try (BufferedReader buff = Files.newBufferedReader(getPath())) {
String line = "";
while ((line = buff.readLine()) != null) {
if (line.contains(this.getStartPattern())) {
desiredLines.add(line);
System.out.println(" \nMatch Found! ");
buff.lines().forEach(streamElement -> {
if (!streamElement.contains(this.getEndPattern())) {
desiredLines.add(streamElement);
} else if (streamElement.contains(this.getEndPattern())) {
throw new IndexOutOfBoundsException("Exit Status 0");
}
});
}
现在的问题是,while 条件在看到第一个“uan”时中断,只捕获消息 ID。我希望代码在通过 uan 时也包含“状态”。
有人可以帮忙吗?
编辑
这是我的预期输出:
uan:123
messageID: 111
uan:123
status:test
应捕获 uan:123 的所有实例
只需使用简单的解析逻辑,仅在看到匹配项时才输出数据uan
。我使用一个布尔变量来跟踪我们是否在给定块内命中了匹配的 uan
。如果是这样,那么我们输出所有行,否则我们不操作并跳过所有内容。
try (BufferedReader buff = Files.newBufferedReader(getPath())) {
String line = "";
String uan = "uan:123";
String begin = "[MESSAGE BEGIN]";
String end = "[MESSAGE END]";
boolean match = false;
while ((line = buff.readLine()) != null) {
if (uan.equals(line)) {
match = true;
}
else if (end.equals(line)) {
match = false;
}
else if (!begin.equals(line) && match) {
System.out.println(line);
}
}
}
请注意,我没有做任何验证来检查是否每个 BEGIN
都被正确的关闭 END
所反映。如果你需要这个,你可以在上面的代码中添加额外的逻辑。
对过滤后的邮件进行分组
您的总体方法似乎不错。我会将其分解为更简单、更直接的逻辑,而不是嵌套循环,例如:
String needle = "uan:123";
String startPattern = "[MESSAGE BEGIN]";
String endPattern = "[MESSAGE END]";
List<List<String>>> result = new ArrayList<>();
try (BufferedReader buff = Files.newBufferedReader(getPath())) {
// Lines and flag for current message
List<String> currentMessage = new ArrayList<>();
boolean messageContainedNeedle = false;
// Read all lines
while (true) {
String line = buff.readLine();
if (line == null) {
break;
}
// Collect current line to message, ignore indicator
if (!line.equals(endPattern) && !line.equals(startPattern)) {
currentMessage.add(line);
}
// Set flag if message contains needle
if (!messageContainedNeedle && line.equals(needle)) {
messageContainedNeedle = true;
}
// Message ends
if (line.equals(endPattern)) {
// Collect if needle was contained
if (messageContainedNeedle) {
result.add(currentMessage);
}
// Prepare for next message
messageContainedNeedle = false;
currentMessage = new ArrayList<>();
}
}
}
更容易阅读和理解。它支持您的消息项以任意顺序出现。此外,生成的 result
仍然将消息分组在 List<List<String>>
中。如果您仍然想要 List<String>
.
,您可以轻松地绘制平面图
结果结构是:
[
["uan:123", "messageID: 111"],
["uan:123", "status: test"]
]
现在很容易准确地实现您想要的输出:
// Variant 1: Nested for-each
result.forEach(message -> message.forEach(System.out::println));
// Variant 2: Flat-map
result.stream().flatMap(List::stream).forEach(System.out::println));
// Variant 3: Without streams
for (List<String> message : result) {
for (String line : message) {
System.out.println(line);
}
}
对所有消息进行分组
如果您省略标志部分,您可以将所有消息解析为该结构,然后轻松地在其上流式传输:
public static List<List<String>> parseMessages(Path path) {
String startPattern = "[MESSAGE BEGIN]";
String endPattern = "[MESSAGE END]";
List<List<String>>> result = new ArrayList<>();
try (BufferedReader buff = Files.newBufferedReader(path)) {
// Data for current message
List<String> currentMessage = new ArrayList<>();
// Read all lines
while (true) {
String line = buff.readLine();
if (line == null) {
break;
}
// Collect current line to message, ignore indicator
if (!line.equals(endPattern) && !line.equals(startPattern)) {
currentMessage.add(line);
}
// Message ends
if (line.equals(endPattern)) {
// Collect message
result.add(currentMessage);
// Prepare for next message
currentMessage = new ArrayList<>();
}
}
}
return result;
}
使用简单明了。例如,过滤 "uan:123"
:
的消息
List<List<String>> messages = parseMessages(getPath());
String needle = "uan:123";
List<List<String>> messagesWithNeedle = messages.stream()
.filter(message -> message.contains(needle))
.collect(Collectors.toList());
结果结构又是:
[
["uan:123", "messageID: 111"],
["uan:123", "status: test"]
]
可以直接在流级联上实现您想要的输出:
messages.stream() // Stream<List<String>>
.filter(message -> message.contains(needle))
.flatMap(List::stream) // Stream<String>
.forEach(System.out::println);
消息容器
一个自然的想法是将消息数据分组到指定的 Message
容器 class 中。类似的东西:
public class Message {
private final Map<String, String> mProperties;
public Message() {
mProperties = new HashMap<>();
}
public String getValue(String key) {
return mProperties.get(key);
}
public void put(String key, String value) {
mProperties.put(key, value);
}
public static Message fromLines(List<String> lines) {
Message message = new Message();
for (String line : lines) {
String[] data = line.split(":");
message.put(data[0].trim(), data[1].trim());
}
return message;
}
// Other methods ...
}
注意方便的 Message#fromLines
方法。使用它你会得到一个 List<Message>
并且处理数据会更方便。
如何创建例如Data
class,包含给定 uan
的所有字段?我可以看到你有一个带有 id(即 uan)的对象,并且有很多消息是针对这个对象的。
我提议使用这种方法并在同一实例中收集所有相关信息(与uan
属于同一对象):
这是Data
class:
final class Data {
private String uan;
private final List<Map<String, String>> events = new LinkedList<>();
public Data(String uan) {
this.uan = uan;
}
public String getUan() {
return uan;
}
public boolean hasUan() {
return uan != null && !uan.isEmpty();
}
public void set(Data data) {
if (data != null)
events.addAll(data.events);
}
public void addEvent(String key, String value) {
if ("uan".equalsIgnoreCase(key))
uan = value;
else
events.add(Collections.singletonMap(key, value));
}
}
这是读取给定文件并检索 Map<String, Data>
的方法,键为 uan
,值是该对象的所有数据:
private static final String BEGIN = "[MESSAGE BEGIN]";
private static final String END = "[MESSAGE END]";
private static final Pattern KEY_VALUE_PATTERN = Pattern.compile("\s*(?<key>[^:]+)\s*:\s*(?<value>[^:]+)\s*");
private static Map<String, Data> readFile(Reader reader) throws IOException {
try (BufferedReader br = new BufferedReader(reader)) {
Data data = null;
Map<String, Data> map = new TreeMap<>();
for (String str; (str = br.readLine()) != null; ) {
if (str.equalsIgnoreCase(BEGIN))
data = new Data(null);
else if (str.equalsIgnoreCase(END)) {
if (data != null && data.hasUan()) {
String uan = data.getUan();
map.putIfAbsent(uan, new Data(uan));
map.get(uan).set(data);
}
data = null;
} else if (data != null) {
Matcher matcher = KEY_VALUE_PATTERN.matcher(str);
if (matcher.matches())
data.addEvent(matcher.group("key"), matcher.group("value"));
}
}
return map;
}
}
最后,客户端的样子是这样的:
Map<String, Data> map = readFile(new FileReader("data.txt"));
这是我正在读取的文件的结构:
[MESSAGE BEGIN]
uan:123
messageID: 111
[MESSAGE END]
[MESSAGE BEGIN]
uan:123
status:test
[MESSAGE END]
我想要做的是,对于给定的 uan,return 它的所有细节,同时保持块结构“MESSAGE BEGIN”“MESSAGE END”。
这是我写的代码:
startPattern= "uan:123"
endPattern= "[MESSAGE END]"
System.out.println("Matching: " + this.getStartPattern());
List<String> desiredLines = new ArrayList<>();
try (BufferedReader buff = Files.newBufferedReader(getPath())) {
String line = "";
while ((line = buff.readLine()) != null) {
if (line.contains(this.getStartPattern())) {
desiredLines.add(line);
System.out.println(" \nMatch Found! ");
buff.lines().forEach(streamElement -> {
if (!streamElement.contains(this.getEndPattern())) {
desiredLines.add(streamElement);
} else if (streamElement.contains(this.getEndPattern())) {
throw new IndexOutOfBoundsException("Exit Status 0");
}
});
}
现在的问题是,while 条件在看到第一个“uan”时中断,只捕获消息 ID。我希望代码在通过 uan 时也包含“状态”。
有人可以帮忙吗?
编辑
这是我的预期输出:
uan:123
messageID: 111
uan:123
status:test
应捕获 uan:123 的所有实例
只需使用简单的解析逻辑,仅在看到匹配项时才输出数据uan
。我使用一个布尔变量来跟踪我们是否在给定块内命中了匹配的 uan
。如果是这样,那么我们输出所有行,否则我们不操作并跳过所有内容。
try (BufferedReader buff = Files.newBufferedReader(getPath())) {
String line = "";
String uan = "uan:123";
String begin = "[MESSAGE BEGIN]";
String end = "[MESSAGE END]";
boolean match = false;
while ((line = buff.readLine()) != null) {
if (uan.equals(line)) {
match = true;
}
else if (end.equals(line)) {
match = false;
}
else if (!begin.equals(line) && match) {
System.out.println(line);
}
}
}
请注意,我没有做任何验证来检查是否每个 BEGIN
都被正确的关闭 END
所反映。如果你需要这个,你可以在上面的代码中添加额外的逻辑。
对过滤后的邮件进行分组
您的总体方法似乎不错。我会将其分解为更简单、更直接的逻辑,而不是嵌套循环,例如:
String needle = "uan:123";
String startPattern = "[MESSAGE BEGIN]";
String endPattern = "[MESSAGE END]";
List<List<String>>> result = new ArrayList<>();
try (BufferedReader buff = Files.newBufferedReader(getPath())) {
// Lines and flag for current message
List<String> currentMessage = new ArrayList<>();
boolean messageContainedNeedle = false;
// Read all lines
while (true) {
String line = buff.readLine();
if (line == null) {
break;
}
// Collect current line to message, ignore indicator
if (!line.equals(endPattern) && !line.equals(startPattern)) {
currentMessage.add(line);
}
// Set flag if message contains needle
if (!messageContainedNeedle && line.equals(needle)) {
messageContainedNeedle = true;
}
// Message ends
if (line.equals(endPattern)) {
// Collect if needle was contained
if (messageContainedNeedle) {
result.add(currentMessage);
}
// Prepare for next message
messageContainedNeedle = false;
currentMessage = new ArrayList<>();
}
}
}
更容易阅读和理解。它支持您的消息项以任意顺序出现。此外,生成的 result
仍然将消息分组在 List<List<String>>
中。如果您仍然想要 List<String>
.
结果结构是:
[
["uan:123", "messageID: 111"],
["uan:123", "status: test"]
]
现在很容易准确地实现您想要的输出:
// Variant 1: Nested for-each
result.forEach(message -> message.forEach(System.out::println));
// Variant 2: Flat-map
result.stream().flatMap(List::stream).forEach(System.out::println));
// Variant 3: Without streams
for (List<String> message : result) {
for (String line : message) {
System.out.println(line);
}
}
对所有消息进行分组
如果您省略标志部分,您可以将所有消息解析为该结构,然后轻松地在其上流式传输:
public static List<List<String>> parseMessages(Path path) {
String startPattern = "[MESSAGE BEGIN]";
String endPattern = "[MESSAGE END]";
List<List<String>>> result = new ArrayList<>();
try (BufferedReader buff = Files.newBufferedReader(path)) {
// Data for current message
List<String> currentMessage = new ArrayList<>();
// Read all lines
while (true) {
String line = buff.readLine();
if (line == null) {
break;
}
// Collect current line to message, ignore indicator
if (!line.equals(endPattern) && !line.equals(startPattern)) {
currentMessage.add(line);
}
// Message ends
if (line.equals(endPattern)) {
// Collect message
result.add(currentMessage);
// Prepare for next message
currentMessage = new ArrayList<>();
}
}
}
return result;
}
使用简单明了。例如,过滤 "uan:123"
:
List<List<String>> messages = parseMessages(getPath());
String needle = "uan:123";
List<List<String>> messagesWithNeedle = messages.stream()
.filter(message -> message.contains(needle))
.collect(Collectors.toList());
结果结构又是:
[
["uan:123", "messageID: 111"],
["uan:123", "status: test"]
]
可以直接在流级联上实现您想要的输出:
messages.stream() // Stream<List<String>>
.filter(message -> message.contains(needle))
.flatMap(List::stream) // Stream<String>
.forEach(System.out::println);
消息容器
一个自然的想法是将消息数据分组到指定的 Message
容器 class 中。类似的东西:
public class Message {
private final Map<String, String> mProperties;
public Message() {
mProperties = new HashMap<>();
}
public String getValue(String key) {
return mProperties.get(key);
}
public void put(String key, String value) {
mProperties.put(key, value);
}
public static Message fromLines(List<String> lines) {
Message message = new Message();
for (String line : lines) {
String[] data = line.split(":");
message.put(data[0].trim(), data[1].trim());
}
return message;
}
// Other methods ...
}
注意方便的 Message#fromLines
方法。使用它你会得到一个 List<Message>
并且处理数据会更方便。
如何创建例如Data
class,包含给定 uan
的所有字段?我可以看到你有一个带有 id(即 uan)的对象,并且有很多消息是针对这个对象的。
我提议使用这种方法并在同一实例中收集所有相关信息(与uan
属于同一对象):
这是Data
class:
final class Data {
private String uan;
private final List<Map<String, String>> events = new LinkedList<>();
public Data(String uan) {
this.uan = uan;
}
public String getUan() {
return uan;
}
public boolean hasUan() {
return uan != null && !uan.isEmpty();
}
public void set(Data data) {
if (data != null)
events.addAll(data.events);
}
public void addEvent(String key, String value) {
if ("uan".equalsIgnoreCase(key))
uan = value;
else
events.add(Collections.singletonMap(key, value));
}
}
这是读取给定文件并检索 Map<String, Data>
的方法,键为 uan
,值是该对象的所有数据:
private static final String BEGIN = "[MESSAGE BEGIN]";
private static final String END = "[MESSAGE END]";
private static final Pattern KEY_VALUE_PATTERN = Pattern.compile("\s*(?<key>[^:]+)\s*:\s*(?<value>[^:]+)\s*");
private static Map<String, Data> readFile(Reader reader) throws IOException {
try (BufferedReader br = new BufferedReader(reader)) {
Data data = null;
Map<String, Data> map = new TreeMap<>();
for (String str; (str = br.readLine()) != null; ) {
if (str.equalsIgnoreCase(BEGIN))
data = new Data(null);
else if (str.equalsIgnoreCase(END)) {
if (data != null && data.hasUan()) {
String uan = data.getUan();
map.putIfAbsent(uan, new Data(uan));
map.get(uan).set(data);
}
data = null;
} else if (data != null) {
Matcher matcher = KEY_VALUE_PATTERN.matcher(str);
if (matcher.matches())
data.addEvent(matcher.group("key"), matcher.group("value"));
}
}
return map;
}
}
最后,客户端的样子是这样的:
Map<String, Data> map = readFile(new FileReader("data.txt"));