从CSV文件解析不同类型的数据格式

时间:2018-11-25 17:55:31

标签: java arrays csv parsing

我仍然是Java编程的初学者,因此如果我使问题变得过于复杂,我会提前道歉。

我的程序是什么? 我正在构建一个基于GUI的程序。该程序的目标是加载CSV,XML或JSON文件,然后程序将数据存储到Array中。然后,数据将显示在文本框中。最终,该程序将能够将数据绘制到图形上。

GUI详细信息:

  • 3个单选按钮-允许用户选择CSV,XML或JSON
  • 加载文件按钮
  • 显示按钮-将数据显示到textArea
  • 显示图形按钮
  • 文本区域

问题:我在将数据存储到数组时遇到问题。我相信这是因为数据的格式。因此,例如,这是CSV文件的前3行:

millis,stamp,datetime,light,temp,vcc
1000, 1273010254, 2010/5/4 21:57:34, 333, 78.32, 3.54
2000, 1273010255, 2010/5/4 21:57:35, 333, 78.32, 3.92
3000, 1273010256, 2010/5/4 21:57:36, 344, 78.32, 3.95

(注意-CSV / XML / JSON文件中有52789000行数据)

CSV-Reader类包含以下方法:读取数据,将其存储到数组中,然后将其存储到dataList中。

从上面的示例中可以看到,某些数据类型有很大不同。我在拆分/解析时间和日期变量时遇到了特别麻烦。

这是我的CSV阅读器类代码目前的样子(再次,我为菜鸟代码表示歉意)。

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class CSVReader {

//create a class that will hold arraylist which will have objects representing all lines of the file

private List<Data> dataList = new ArrayList<Data>();
private String path;

public List<Data> getDataList() {
    return dataList;
}

public String getPath() {
    return path;
}
public void setPath(String path) {
    this.path = path;
}

//Create a method to read through the csv stored in the path
//Create the list of data and store in the dataList

public void readCSV() throws IOException{

    //i will create connection with the file, in the path
    BufferedReader in  = new BufferedReader(new FileReader(path));  

    String line = null;
    line = in.readLine();

    while((line = in.readLine())!=null){

        //I need to split and store in the temporary variable and create an object


        String[] splits = line.split("\\s*(=>|,|\\s)\\s*");

        long millis = Long.parseLong(splits[0].trim());
        long stamp = Long.parseLong(splits[1].trim());
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/M/d HH:mm:ss");
        System.out.println(splits[2].trim());
        LocalDateTime dateTime = LocalDateTime.parse(splits[2].trim(), formatter);
        LocalDate dateTime = dateTime.toLocalDate();
        LocalTime time = dateTime.toLocalTime();
        int light = Integer.parseInt(splits[3].trim());
        double temp = Double.parseDouble(splits[4].trim());
        double vcc = Double.parseDouble(splits[5].trim());

        Data d = new Data(millis,stamp,datetime,light,temp,vcc);//uses constructor


        //final job is to add this object 'd' onto the dataList
        dataList.add(d);

    }//end of while loop

}

任何帮助将不胜感激!

编辑1-我认为日期和时间是单独的CSV标头。他们不是。因此,时间变量已从程序中删除。它已替换为datetime变量。

编辑2-我的程序现在正在读取CSV文件,直到csv的第15行

  

27000,1273010280, 2010/5/4 21:58:0 ,288,77.74,3.88

控制台错误

Exception in thread "AWT-EventQueue-0" 
java.time.format.DateTimeParseException: Text **'2010/5/4 21:58:0'** could not 
be parsed at index 15
at java.time.format.DateTimeFormatter.parseResolved0(Unknown Source)
at java.time.format.DateTimeFormatter.parse(Unknown Source)
at java.time.LocalDateTime.parse(Unknown Source)
at CSVReader.readCSV(CSVReader.java:55)
at GUI$2.actionPerformed(GUI.java:85)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$500(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

4 个答案:

答案 0 :(得分:1)

由于您的print(pd.to_datetime(df['date'].astype(str) + ' ' + df['time'].astype(str))) 0 2011-01-01 12:48:20 1 2014-01-01 12:30:45 dtype: datetime64[ns] 列是字符串,因此您需要将其解析为日期对象。这取决于您的Java版本。如果您使用的是Java 8,则可以使用新的日期类。也许this answer可以帮助您。

看起来您在CSV标头中有两个单独的列,datedate,但是这两行中的日期和时间在其中一列。这可能是您问题的一部分。您需要确定它们是两列还是一列,但是无论如何都可以使用Java库对其进行解析。

此外,最好使用经过Apache Commons CSV之类的经过战斗测试的CSV库,而不是滚动自己的CSV解析器。在有限的情况下,您可能会遇到麻烦,但是CSV并不像它第一次出现时那样简单。

答案 1 :(得分:1)

ISO 8601

  

已解决,由于我的CSV格式不符合正确的日期和时间格式,因此程序崩溃了。

在将日期时间值作为文本交换时,请使用标准的ISO 8601格式,而不要发明自己的格式。明智地设计了它们,使其易于通过机器解析,并且易于跨文化的人类阅读。因此,2010-05-04T21:57:34,而不是2010/5/4 21:57:34

java.time 类在解析/生成字符串时默认使用ISO 8601格式。

数据类型

日期时间

数据Feed的第二列和第三列表示相同的内容:带有日期的日期。第一个是自纪元参考日期1970-01-01T00:00Z(Z表示UTC)以来的秒数。

因此同时包含这两者是很愚蠢的。如上所述,第3列的格式选择不当。在我看来,使用“从纪元计数”的第二列方法也不是一个好的选择,因为它并不明显,没有人可以理解其含义,因此错误变得不明显,从而使调试和记录变得困难。

要处理我们拥有的内容,可以将距秒的秒数解析为Instant。该课程代表UTC的时刻。

Instant instant = Instant.ofEpochMilli( 1_273_010_254L ) ;

您的第3列给出了日期和时间,但省略了时区或UTC偏移量的指示。由于它与UTC 1970年第一时刻的秒数相匹配时与第二列匹配,因此我们知道它的值适用于UTC。忽略此类信息是不明智的做法,例如拥有一个没有货币指标的货币金额。

理想情况下,两列均应替换为ISO 8601格式的字符串,例如2010-05-04T21:57:34Z包括Z以表示UTC。

如果我们不得不在不知道要用于UTC的情况下解析第三列,则将其解析为LocalDateTime,即具有时间的日期,但缺少时区或偏移量。我们需要定义一种格式设置模式以匹配您的输入。

DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu/M/d HH:mm:ss" );
LocalDateTime localDateTime = LocalDateTime.parse( "2010/5/4 21:57:34" , f );

BigDecimal

为了精确起见,您的小数部分数字应表示为BigDecimal对象。切勿在关心准确性的地方使用double / Doublefloat / Float。这些类型使用floating-point技术trades away accuracy来提高执行速度。相反,BigDecimal慢但准确。

从字符串中解析BigDecimal

new BigDecimal ( "78.32" ) 

Apache Commons CSV

在经过良好测试的代码已经存在时,请勿编写代码。已经编写了可读取CSV / Tab-delimited文件的库。

logo for the Apache Commons CSV project

我将Apache Commons CSV用于此类工作。这些格式有几种变体,均由该库处理。

这是示例代码。首先定义一个类来保存您的数据,这里命名为Reading

Reading.java

package com.basilbourque.example;

import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;

public class Reading {
    private Integer millis;
    private Instant instant;
    private LocalDateTime localDateTime;
    private Integer light;
    private BigDecimal temp;
    private BigDecimal vcc;

    public Reading ( Integer millis , Instant instant , LocalDateTime localDateTime , Integer light , BigDecimal temp , BigDecimal vcc ) {
        // TODO: Add checks for null arguments: Objects.requireNonNull( … ).
        this.millis = millis;
        this.instant = instant;
        this.localDateTime = localDateTime;
        this.light = light;
        this.temp = temp;
        this.vcc = vcc;
    }

    @Override
    public String toString ( ) {
        return "com.basilbourque.example.Reading{" +
                "millis=" + millis +
                ", instant=" + instant +
                ", localDateTime=" + localDateTime +
                ", light=" + light +
                ", temp=" + temp +
                ", vcc=" + vcc +
                '}';
    }
}

示例数据文件:

millis,stamp,datetime,light,temp,vcc
1000, 1273010254, 2010/5/4 21:57:34, 333, 78.32, 3.54
2000, 1273010255, 2010/5/4 21:57:35, 333, 78.32, 3.92
3000, 1273010256, 2010/5/4 21:57:36, 344, 78.32, 3.95

现在调用Commons CSV解析该数据,实例化Reading对象,并收集它们。

DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu/M/d HH:mm:ss" );

List < Reading > readings = new ArrayList <>( 3 );
Reader reader = null;
try {
    reader = new FileReader( "/Users/basilbourque/data.csv" );
    Iterable < CSVRecord > records = CSVFormat.RFC4180.withIgnoreSurroundingSpaces( true ).withHeader().parse( reader );
    for ( CSVRecord record : records ) {
        // Grab inputs
        String millisInput = record.get( "millis" );
        String stampInput = record.get( "stamp" );
        String datetimeInput = record.get( "datetime" );
        String lightInput = record.get( "light" );
        String tempInput = record.get( "temp" );
        String vccInput = record.get( "vcc" );

        // Parse inputs
        Integer millis = Integer.valueOf( millisInput );
        Instant instant = Instant.ofEpochSecond( Integer.valueOf( stampInput ) );
        LocalDateTime localDateTime = LocalDateTime.parse( datetimeInput , f );
        Integer light = Integer.valueOf( lightInput );
        BigDecimal temp = new BigDecimal( tempInput );
        BigDecimal vcc = new BigDecimal( vccInput );

        // Construct object
        Reading r = new Reading( millis , instant , localDateTime , light , temp , vcc );

        // Collect object
        readings.add( r );
    }
} catch ( FileNotFoundException e ) {
    e.printStackTrace();
} catch ( IOException e ) {
    e.printStackTrace();
}

System.out.println( readings );
  

[com.basilbourque.example.Reading {millis = 1000,Instant = 2010-05-04T21:57:34Z,localDateTime = 2010-05-04T21:57:34,light = 333,temp = 78.32,vcc = 3.54},com.basilbourque.example.Reading {millis = 2000,Instant = 2010-05-04T21:57:35Z,localDateTime = 2010-05-04T21:57:35,light = 333,temp = 78.32,vcc = 3.92 },com.basilbourque.example.Reading {millis = 3000,Instant = 2010-05-04T21:57:36Z,localDateTime = 2010-05-04T21:57:36,light = 344,temp = 78.32,vcc = 3.95} ]

关于您的提及:

  

将数据存储到数组

您在代码中使用的是ArrayList,而不是数组。请参阅Oracle教程for listsfor arrays了解不同之处。通常最好使用Java Collections framework。在大小和速度真正重要的地方,我们可以选择一个数组。


关于 java.time

java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat

目前位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*类。

在哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

答案 2 :(得分:0)

  1. 似乎您在一行中有5个令牌(不是6),

  2. 您正试图将日期字符串解析为双精度,

在您的代码中进行了一些修改,以下是为您执行的一项工作:

String[] splits = line.split(",");// line.split("\\s*(=>|,|\\s)\\s*");

long millis = Long.parseLong(splits[0].trim());
long stamp = Long.parseLong(splits[1].trim());
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/M/d HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.parse(splits[2].trim(), formatter);
LocalDate date = dateTime.toLocalDate();
LocalTime time = dateTime.toLocalTime();
int light = Integer.parseInt(splits[3].trim());
double temp = Double.parseDouble(splits[4].trim());
double vcc = Double.parseDouble(splits[5].trim());

答案 3 :(得分:-1)

您正尝试将date解析为数字,并且这是一个String字段,也许您可​​以使用此

Date date=new SimpleDateFormat("yyyy/M/D HH:mm:ss").parse(splits[2]);

现在有了日期,您可以根据需要进行转换