Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/401.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用CsvBeanReader读取列数可变的CSV文件_Java_Csv_Supercsv - Fatal编程技术网

Java 使用CsvBeanReader读取列数可变的CSV文件

Java 使用CsvBeanReader读取列数可变的CSV文件,java,csv,supercsv,Java,Csv,Supercsv,所以我正在分析一个.csv文件。我听取了StackOverflow上另一个线程的建议,并下载了SuperCSV。我终于让几乎所有的东西都工作了,但现在我遇到了一个似乎很难修复的bug 出现此问题的原因是最后两列数据可能已填充,也可能未填充。下面是一个.csv文件的示例,第一行缺少最后一列,第二行完全完成: 2012:07:25,11:48:20922,“uLog.exe”,键 按下,1246341,-1.00,-1.00,1.00,换档 2012:07:25,11:48:21094,“uLog.

所以我正在分析一个.csv文件。我听取了StackOverflow上另一个线程的建议,并下载了SuperCSV。我终于让几乎所有的东西都工作了,但现在我遇到了一个似乎很难修复的bug

出现此问题的原因是最后两列数据可能已填充,也可能未填充。下面是一个.csv文件的示例,第一行缺少最后一列,第二行完全完成:

2012:07:25,11:48:20922,“uLog.exe”,键 按下,1246341,-1.00,-1.00,1.00,换档 2012:07:25,11:48:21094,“uLog.exe”,键 按下,1246341,-1.00,-1.00,1.00,b,换档

根据我对的理解,若列数可变,则无法用填充JavaBean。这看起来非常愚蠢,因为我觉得在初始化Bean时,应该允许这些缺少的列为null或其他一些默认值

以下是我为解析器编写的完整代码,以供参考:

public class ULogParser {

String uLogFileLocation;
String screenRecorderFileLocation;

private static final CellProcessor[] cellProcessor = new CellProcessor[] {
    new ParseDate("yyyy:MM:dd"),
    new ParseDate("HH:mm:ss"),
    new ParseDate("SSS"),
    new StrMinMax(0, 100),
    new StrMinMax(0, 100),
    new StrMinMax(0, 100),
    new ParseInt(),
    new ParseInt(),
    new ParseDouble(),
    new ParseDouble(),
    new ParseDouble(),
    new StrMinMax(0, 100),
    new StrMinMax(0, 100),
};

public String[] header = {"Date", "Time", "Msec", "Application", "Window", "Message", "X", "Y", "RelDist", "TotalDist", "Rate", "Extra1", "Extra2"}; 

public ULogParser(String uLogFileLocation, String screenRecorderFileLocation)
{
    this.uLogFileLocation = uLogFileLocation;
    this.screenRecorderFileLocation = screenRecorderFileLocation;
}

public void parse()
{
    try {
        ICsvBeanReader reader = new CsvBeanReader(new BufferedReader(new FileReader(uLogFileLocation)), CsvPreference.STANDARD_PREFERENCE);
        reader.getCSVHeader(false); //parse past the header
        Entry entry;
        entry = reader.read(Entry.class, header, cellProcessor);
        System.out.println(entry.Application);
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

public void sendToDB()
{
    Query query = new Query();
}
}
以及Entry类的代码:

public class Entry
{
private Date Date;
private Date Time;
private Date Msec;
private String Application;
private String Window;
private String Message;
private int X;
private int Y;
private double RelDist;
private double TotalDist;
private double Rate;
private String Extra1;
private String Extra2;

public Date getDate() { return Date; }
public Date getTime() { return Time; }
public Date getMsec() { return Msec; }
public String getApplication() { return Application; }
public String getWindow() { return Window; }
public String getMessage() { return Message; }
public int getX() { return X; }
public int getY() { return Y; }
public double getRelDist() { return RelDist; }
public double getTotalDist() { return TotalDist; }
public double getRate() { return Rate; }
public String getExtra1() { return Extra1; }
public String getExtra2() { return Extra2; }

public void setDate(Date Date) { this.Date = Date; }
public void setTime(Date Time) { this.Time = Time; }
public void setMsec(Date Msec) { this.Msec = Msec; }
public void setApplication(String Application) { this.Application = Application; }
public void setWindow(String Window) { this.Window = Window; }
public void setMessage(String Message) { this.Message = Message; }
public void setX(int X) { this.X = X; }
public void setY(int Y) { this.Y = Y; }
public void setRelDist(double RelDist) { this.RelDist = RelDist; }
public void setTotalDist(double TotalDist) { this.TotalDist = TotalDist; }
public void setRate(double Rate) { this.Rate = Rate; }
public void setExtra1(String Extra1) { this.Extra1 = Extra1; }
public void setExtra2(String Extra2) { this.Extra2 = Extra2; }

public Entry(){}
}
以及我收到的异常(注意,这与我上面的示例不同,缺少最后两列):

线程“main”中的异常值数组(大小12)必须与处理器数组(大小13)匹配:您可能正在读取的CSV行的列数与指定的CellProcessor数不同上下文:行:2列:0原始行: [2012:07:2511:48:05740,uLog.exe,,日志记录已开始,-1,-1.00,-1.00,-1.00,-1.00,] 有问题的处理器:null 位于org.supercsv.util.util.processStringList(未知源) 位于org.supercsv.io.CsvBeanReader.read(未知来源) 在processing.ULogParser.parse处(ULogParser.java:59) 位于ui.ParseImplicitData.main(ParseImplicitData.java:15) 是的,编写所有这些getter和setter是一件让人头疼的事。另外,我很抱歉,我可能在使用SuperCSV时没有完美的约定(比如如果你只想使用未修改的字符串,那么使用什么CellProcessor),但你明白了。而且,这段代码显然不完整。现在,我只是想成功地检索一行数据

此时,我想知道是否可以使用CsvBeanReader来实现我的目的。如果没有,我有点失望,因为CsvListReader(我会发布超链接,但StackOverflow也不允许我这么做,也很愚蠢)与根本不使用API,只使用Scanner.next()一样简单


任何帮助都将不胜感激。提前谢谢

超级SV是开源的。如果要添加功能,例如使用可变数量的尾随字段处理输入,基本上有两个选项:

  • 在SourceForge网站上发布支持请求,希望作者同意并有时间这样做
  • 下载源代码,根据您的喜好进行更改,并将更改贡献给项目

  • 这就是开源的工作原理。

    编辑:更新

    请注意,Super CSV 2.0.0-beta-1中的API已更改(代码示例基于1.52)。所有读卡器上的
    getCSVHeader()
    方法现在都是
    getHeader()
    (与编写器上的
    writeHeader
    一致)

    此外,
    SuperCSVException
    已重命名为
    SuperCSVException



    编辑:超级CSV 2.1.0的更新

    从2.1.0版开始,可以使用新的
    executeProcessors()
    方法在读取一行CSV后执行单元处理器。有关更多信息,请参见项目网站上的。请注意,这仅与
    CsvListReader
    相关,因为它是唯一允许可变列长度的读取器


    您是正确的-
    CsvBeanReader
    不支持列数可变的CSV文件。根据大多数CSV规范(包括),每行的列数必须相同

    出于这个原因(作为超级CSV开发人员),我不愿意将此功能添加到超级CSV中。如果您能想出一种优雅的方式来添加它,那么可以在项目的SourceForge站点上提出建议。它可能意味着一个扩展到
    CsvBeanReader
    的新读取器:它必须将读取和映射/处理分为两个单独的方法(除非知道有多少列,否则不能对bean的字段进行任何处理或映射)

    简单解 解决这个问题的简单方法是(如果您控制了正在使用的CSV文件),在编写CSV文件时只需添加一个空白列(示例中的第一行末尾有一个逗号,表示最后一列为空)。这样,您的CSV文件将是有效的(每行上的列数相同),并且您可以使用
    CsvBeanReader

    如果这不可能,那么一切都不会失去

    花式解决方案 您可能已经意识到,
    CsvBeanReader
    使用名称映射将CSV文件中的每一列与bean中的字段相关联,并使用CellProcessor数组处理每一列。换句话说,如果您想使用它,您必须知道有多少列(以及它们代表什么)

    另一方面,
    CsvListReader
    非常原始,可以读取不同长度的行(因为它不需要处理或映射它们)

    因此,您可以将
    CsvBeanReader
    的所有功能与
    CsvListReader
    结合起来(如下例所示),方法是使用两个读卡器并行读取文件:使用
    CsvListReader
    计算有多少列,使用
    CsvBeanReader
    进行处理/映射

    注意,这使得假设只有生日列可能不存在(也就是说,如果你不知道缺少哪一列,它将不起作用)

    我希望这有帮助

    哦,那有什么理由让这个领域 Exception in thread "main" The value array (size 12) must match the processors array (size 13): You are probably reading a CSV line with a different number of columns than the number of cellprocessors specified context: Line: 2 Column: 0 Raw line: [2012:07:25, 11:48:05, 740, uLog.exe, , Logging started, -1, -1, -1.00, -1.00, -1.00, ] offending processor: null at org.supercsv.util.Util.processStringList(Unknown Source) at org.supercsv.io.CsvBeanReader.read(Unknown Source) at processing.ULogParser.parse(ULogParser.java:59) at ui.ParseImplicitData.main(ParseImplicitData.java:15)
    package example;
    
    import java.io.StringReader;
    import java.util.Date;
    
    import org.supercsv.cellprocessor.ParseDate;
    import org.supercsv.cellprocessor.ift.CellProcessor;
    import org.supercsv.exception.SuperCSVException;
    import org.supercsv.io.CsvBeanReader;
    import org.supercsv.io.CsvListReader;
    import org.supercsv.io.ICsvBeanReader;
    import org.supercsv.io.ICsvListReader;
    import org.supercsv.prefs.CsvPreference;
    
    public class VariableColumns {
    
        private static final String INPUT = "name,birthDate,city\n"
            + "John,New York\n" 
            + "Sally,22/03/1974,London\n" 
            + "Jim,Sydney";
    
        // cell processors
        private static final CellProcessor[] NORMAL_PROCESSORS = 
        new CellProcessor[] {null, new ParseDate("dd/MM/yyyy"), null };
        private static final CellProcessor[] NO_BIRTHDATE_PROCESSORS = 
        new CellProcessor[] {null, null };
    
        // name mappings
        private static final String[] NORMAL_HEADER = 
        new String[] { "name", "birthDate", "city" };
        private static final String[] NO_BIRTHDATE_HEADER = 
        new String[] { "name", "city" };
    
        public static void main(String[] args) {
    
            // using bean reader and list reader together (to read the same file)
            final ICsvBeanReader beanReader = new CsvBeanReader(new StringReader(
                    INPUT), CsvPreference.STANDARD_PREFERENCE);
            final ICsvListReader listReader = new CsvListReader(new StringReader(
                    INPUT), CsvPreference.STANDARD_PREFERENCE);
    
            try {
                // skip over header
                beanReader.getCSVHeader(true);
                listReader.getCSVHeader(true);
    
                while (listReader.read() != null) {
    
                    final String[] nameMapping;
                    final CellProcessor[] processors;
    
                    if (listReader.length() == NORMAL_HEADER.length) {
                        // all columns present - use normal header/processors
                        nameMapping = NORMAL_HEADER;
                        processors = NORMAL_PROCESSORS;
    
                    } else if (listReader.length() == NO_BIRTHDATE_HEADER.length) {
                        // one less column - birth date must be missing
                        nameMapping = NO_BIRTHDATE_HEADER;
                        processors = NO_BIRTHDATE_PROCESSORS;
    
                    } else {
                        throw new SuperCSVException(
                                "unexpected number of columns: "
                                        + listReader.length());
                    }
    
                    // can now use CsvBeanReader safely 
                    // (we know how many columns there are)
                    Person person = beanReader.read(Person.class, nameMapping,
                            processors);
    
                    System.out.println(String.format(
                            "Person: name=%s, birthDate=%s, city=%s",
                            person.getName(), person.getBirthDate(),
                            person.getCity()));
    
                }
            } catch (Exception e) {
                // handle exceptions here
                e.printStackTrace();
            } finally {
                // close readers here
            }
        }
    
        public static class Person {
    
            private String name;
            private Date birthDate;
            private String city;
    
            public String getName() {
                return name;
            }
    
            public void setName(String name) {
                this.name = name;
            }
    
            public Date getBirthDate() {
                return birthDate;
            }
    
            public void setBirthDate(Date birthDate) {
                this.birthDate = birthDate;
            }
    
            public String getCity() {
                return city;
            }
    
            public void setCity(String city) {
                this.city = city;
            }
        }
    
    }
    
    class TestBean {
    
    // if the value parsed in the quantity column is "?" or "-", it will be replaced by null.
    @NullString(nulls = { "?", "-" })
    // if a value resolves to null, it will be converted to the String "0".
    @Parsed(defaultNullRead = "0")
    private Integer quantity;   // The attribute type defines which conversion will be executed when processing the value.
    // In this case, IntegerConversion will be used.
    // The attribute name will be matched against the column header in the file automatically.
    
    @Trim
    @LowerCase
    // the value for the comments attribute is in the column at index 4 (0 is the first column, so this means fifth column in the file)
    @Parsed(index = 4)
    private String comments;
    
    // you can also explicitly give the name of a column in the file.
    @Parsed(field = "amount")
    private BigDecimal amount;
    
    @Trim
    @LowerCase
    // values "no", "n" and "null" will be converted to false; values "yes" and "y" will be converted to true
    @BooleanString(falseStrings = { "no", "n", "null" }, trueStrings = { "yes", "y" })
    @Parsed
    private Boolean pending;
    ...
    }
    
    // BeanListProcessor converts each parsed row to an instance of a given class, then stores each instance into a list.
    BeanListProcessor<TestBean> rowProcessor = new BeanListProcessor<TestBean>(TestBean.class);
    CsvParserSettings parserSettings = new CsvParserSettings();
    parserSettings.setRowProcessor(rowProcessor);
    //Uses the first valid row of the CSV to assign names to each column
    parserSettings.setHeaderExtractionEnabled(true);
    
    CsvParser parser = new CsvParser(parserSettings);
    parser.parse(new FileReader(yourFile));
    
    // The BeanListProcessor provides a list of objects extracted from the input.
    List<TestBean> beans = rowProcessor.getBeans();