【发布时间】:2023-03-30 00:07:01
【问题描述】:
对于一个 java 模拟项目,我有一个日志文件,用于跟踪单次运行期间发生的所有事情。我通过将自定义 TimeLog.class 的对象添加到列表来做到这一点。模拟完成后,我循环遍历该列表并为每个条目编写一个字符串 ArrayList,然后使用我在网上找到的以下“CSVHelper”类将其写入 csv 文件(感谢 Keke Chen)。
创建字符串的ArrayList:
public static void saveData(List<TimeLog> data, String fileName, Simulator simulator,
ArrayList<String> header) throws Exception {
File csvFile = new File(simulator.getOutputFolder()+fileName);
FileOutputStream fos = new FileOutputStream(csvFile);
Writer fw = new OutputStreamWriter(fos, "UTF-8");
CSVHelper.writeLine(fw, header);
for (TimeLog entry : data) {
ArrayList<String> row = new ArrayList<>();
row.add(String.valueOf(entry.getTime())); //time of entry, double
row.add(String.valueOf(entry.getMessageType())); //type of message (SETUP, DEBUG, EVENT, ERROR)
if (entry.getEvent() == null) {
row.add("");
} else {
row.add(entry.getEvent().getTypeName()); //event type if event
}
row.add(entry.getLog()); //message
CSVHelper.writeLine(fw, row);
}
fw.flush();
fw.close();
}
CSVHelper.writeLine:
/** Class for processing csv file.
* created by Keke Chen (keke.chen@wright.edu)
* For Cloud Computing Labs
* Feb. 2014
*/
public static void writeLine(Writer w, ArrayList<String> values) throws Exception {
boolean firstVal = true;
for (String val : values) {
if (!firstVal) {
w.write(",");
}
w.write("\"");
for (int i=0; i<val.length(); i++) {
char ch = val.charAt(i);
if (ch=='\"') {
w.write("\""); //extra quote
}
w.write(ch);
}
w.write("\"");
firstVal = false;
}
w.write("\n");
}
出于调试目的,我捕获模拟器抛出的异常,将有关它们的信息添加到我的日志列表中,写入日志文件,然后再次抛出异常。这样,当出现问题时,我可以在日志文件中查看发生了什么。
直到最近这还不错,但现在我在编写过程中的第二个 for 循环期间得到了 NullPointerException。出于某种原因,这条线
for (int i=0; i<val.length(); i++)
在字符串中途抛出异常。如果我打开创建的日志文件,最后一个条目也有一半完成的消息或时间戳(例如:“工具 X 是 res”而不是“工具 X 为项目 Y 保留”或“212”而不是“ 212312.16")。
在编写 csv 文件、在 java 中保存列表、在字符串字符上使用 for 循环或其他我不知道的事情上是否有任何限制?这让我很头疼。
Ciao, 罗宾
编辑:根据要求,列表条目的示例:
//initiating:
private List<TimeLog> logging = new ArrayList<>();
//message:
public void message(MessageType mt, String s, Event e) {
if (saveLog) {
logFile.add(new TimeLog(now(), mt, s, e)); //now() gets the current time of the simulator
}
}
//example:
Event e = new ReservationEvent(simulator.now(), X, Y);
simulator.message(MessageType.EVENT, "Tool "+X.getId()+" was reserved for item "+Y+getId(), e);
这是堆栈跟踪:
java.lang.NullPointerException
at model.simulator.transport.input.CSVHelper.writeLine(CSVHelper.java:27)
at model.simulator.TimeLog.saveData(TimeLog.java:73)
at model.simulator.Simulator.endSimulation(Simulator.java:98)
at model.simulator.Simulator.runSimulation(Simulator.java:64)
at test.simulator.compare.TestFullFab.testRunning(TestFullFab.java:147)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:539)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:761)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:461)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:207)
由于 Emax 的评论,我注意到了其他一些事情。我加了
System.out.println("0:"+row.get(0)+", 1:"+row.get(1)+", 2:"+row.get(2)+", 3:"+row.get(3));
在 saveData 方法中,就在 CSVHelper.writeLine(fw,row) 之前,在字符串被写入之前查看字符串列表。这导致了控制台中的完整日志列表,以及没有(或部分)写入 csv 文件的日志。不知何故,当 writeLine 抛出异常时,TimeLog for 循环继续。
EDIT2:TimeLog 的结构:
public class TimeLog {
private double time;
private MessageType mt;
private String log;
private Event e;
public TimeLog(double time, MessageType mt, String log, Event e) {
this.time = time;
this.mt = mt;
this.log = log;
this.e = e;
}
//see full method above
public static void saveData(List<TimeLog> data, String fileName, Simulator simulator,
ArrayList<String> header) throws Exception { ...
//further more, there are some getters
}
作为 System.out.println() 行结果的控制台输出示例:
0:27131.490112666303, 1:EVENT, 2:TOOLEVENT, 3:Tool input event, item 3998 has gone into tool QSD301
请记住,0:,1:,2: 和 3: 不在原始列表中,而是添加到 println() 方法中。
更新:使用 Emax 打印返回 null 的值的想法揭示了问题。 val 的值确实变成了 null,它发生在 NullPointerException(没有消息),所以 e.getMessage() 返回 null。奇怪的是,Writer 根本不会将所有值写入 csv 文件中,但似乎有某种延迟(也许它在实际将其写入文件之前首先收集了默认数量的字符?)。因此,我认为 NullPointer 发生在通过 val 的循环期间,而它已经完成了。感谢您的帮助!
【问题讨论】:
-
异常的 StackTrace 是要检查的第一个信息源!至少要获得抛出异常的正确行。
-
@MRRommers 发布 TimeLog 类的结构以及您作为列表传递给 saveData 方法的实例示例
-
@MRRommers 也许我想通了!堆栈跟踪指向第 73 行的 TimeLog.java,检查 (ArrayList
标头) 是否包含空值!!!! -
很抱歉让您失望了,标题不包含空值,此行也正确写入 csv 文件,并且 writeLine 方法比第一行更远。 TimeLog 第 73 行包含: CSVHelper.writeLine(fw, row);
-
顺便说一句,你为什么不把
TimeLog字段设为final?另外,涉及多少线程?列表在填充时是否有可能被许多线程修改?
标签: java csv for-loop nullpointerexception string-length