【问题标题】:CSVhelper writeLine, null pointer halfway during a for-loopCSVhelper writeLine,for循环中途的空指针
【发布时间】: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


【解决方案1】:

导致该行出现 NullPointerException 的唯一可能原因是 val 为 null,因为 val 是从列表中获取的,问题冒泡到:

    row.add(String.valueOf(entry.getTime()));
    row.add(String.valueOf(entry.getMessageType()));
    row.add(entry.getEvent().getTypeName());
    row.add(entry.getLog());

其中一个添加是在列表中添加一个空值,您应该检查其中之一。 但发布完整的跟踪记录,包括(如果存在)“原因”

更新

尝试像这样修改writeLine:

        [...]
        if (!firstVal) {
            w.write(",");
        }
        w.write("\"");

        // EDIT HERE
        if (val == null) {
            System.out.println("This val is null: " + values);
        }
        // .....

        for (int i = 0; i < val.length(); i++) {
        [...]

使用导致 NullPointerException 的案例发布输出

【讨论】:

  • 这就是我的想法,但创建的文件在最终单元格中也包含字符串值。刚才,最后一个单元格包含消息的开头,但中途停止。变量 val 在 for 循环期间没有改变,并且包含可写字符,但循环只是停止。
  • @MRommers 嗨,您能否发布 straktrace 和您传递给方法的 List 数据的示例
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-21
  • 2020-02-07
  • 2018-07-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多