【问题标题】:How to read from particular header in opencsv?如何从opencsv中的特定标头读取?
【发布时间】:2015-07-14 18:08:05
【问题描述】:

我有一个 csv 文件。我想从中提取特定的列。例如: 说,我有 csv:

id1,caste1,salary,name1
63,Graham,101153.06,Abraham
103,Joseph,122451.02,Charlie
63,Webster,127965.91,Violet
76,Smith,156150.62,Eric
97,Moreno,55867.74,Mia
65,Reynolds,106918.14,Richard

如何使用 opencsv 从 header caste1 中读取数据?

【问题讨论】:

    标签: java opencsv


    【解决方案1】:

    Magnilex 和 Sparky 的正确之处在于 CSVReader 不支持按列名读取值。但话虽如此,有两种方法可以做到这一点。

    鉴于您有列名并且默认的 CSVReader 读取标题,您可以在第一个标题中搜索该位置,然后从那里开始使用它;

    private int getHeaderLocation(String[] headers, String columnName) {
       return Arrays.asList(headers).indexOf(columnName);
    }
    

    所以你的方法看起来像(省略了很多你需要放入的错误检查)

    CSVReader reader = new CSVReader(new FileReader("yourfile.csv"));
    String [] nextLine;
    int columnPosition;
    
    nextLine = reader.readNext();
    columnPosition = getHeaderLocation(nextLine, "castle1");
    
    while ((nextLine = reader.readNext()) != null && columnPosition > -1) {
       // nextLine[] is an array of values from the line
       System.out.println(nextLine[columnPosition]);
    }
    

    如果您时间紧迫,并且您只关心一个专栏,我只会执行上述操作。这是因为 openCSV 可以使用CsvToBean 类和HeaderColumnNameMappingStrategy 直接转换为具有与标题列名称相同的变量的对象。

    因此,首先您将定义一个具有字段的类(实际上您只需要放入您想要的字段 - 额外的被忽略,缺少的是空值或默认值)。

    public class CastleDTO {
       private int id1;
       private String castle1;
       private double salary;
       private String name1;
    
       // have all the getters and setters here....
    }
    

    那么你的代码会是这样的

    CSVReader reader = new CSVReader(new FileReader("yourfile.csv"));
    HeaderColumnNameMappingStrategy<CastleDTO> castleStrategy = new HeaderColumnNameMappingStrategy<CastleDTO>();
    CsvToBean<CastleDTO> csvToBean = new CsvToBean<CastleDTO>();
    
    List<CastleDTO> castleList = csvToBean.parse(castleStrategy, reader);
    
    for (CastleDTO dto : castleList) {
       System.out.println(dto.getCastle1());
    }
    

    【讨论】:

      【解决方案2】:

      opencsv 中没有用于按名称读取列的内置功能。

      official FAQ example 有以下关于如何从文件中读取的示例:

      CSVReader reader = new CSVReader(new FileReader("yourfile.csv"));
      String [] nextLine;
      while ((nextLine = reader.readNext()) != null) {
         // nextLine[] is an array of values from the line
         System.out.println(nextLine[0] + nextLine[1] + "etc...");
      }
      

      您只需通过使用nextLine[1] 访问行来获取每行第二列中的值(请记住,数组索引是从零开始的)。

      因此,在您的情况下,您可以简单地从第二行读取:

      CSVReader reader = new CSVReader(new FileReader("yourfile.csv"));
      String [] nextLine;
      while ((nextLine = reader.readNext()) != null) {
         System.out.println(nextLine[1]);
      }
      

      有关从标题确定列索引的更复杂方法,请参阅the answer from Scott Conway

      【讨论】:

      • 我必须从标头名称(caste1)中获取,而不是从索引中获取。我该怎么办?
      • @user2080429 我不认为这种行为是内置在 OpenCSV 中的。使用reader.readNext() 读取第一行,确定它有哪个索引,然后使用我的解决方案。
      • @Magnilex 我可以直接通过标题名称获取值吗,
      • @PranayKumbhalkar 似乎没有对此的直接支持,但请查看 Scotts 的回答:stackoverflow.com/a/31440044/1421925
      • @Magnilex Scotts 编写的代码不起作用,但 scotts 使用的概念对我有用。首先,他获取标题位置并从数组中获取特定位置的值
      【解决方案3】:

      来自opencsv docs

      从 4.2 版开始,还有另一种方便的读取 CSV 文件的方法,甚至不需要创建特殊的类。如果您的 CSV 文件有标题,您只需初始化一个 CSVReaderHeaderAware 并开始将值作为地图读取:

        reader = new CSVReaderHeaderAware(new FileReader("yourfile.csv"));
        record = reader.readMap();
      

      .readMap() 将返回一条记录。您需要反复调用.readMap() 以获取所有记录,直到它运行到最后(或第一个空行)时您得到null,例如:

      Map<String, String> values;
      
      while ((values = reader.readMap()) != null) {
      
          // consume the values here
      
      }
      

      该类还有另一个允许更多自定义的构造函数,例如:

      CSVReaderHeaderAware reader = new CSVReaderHeaderAware(
              new InputStreamReader(inputStream),
              0,      // skipLines
              parser, // custom parser
              false,  // keep end of lines
              true,   // verify reader
              0,      // multiline limit
              null    // null for default locale
      );
      

      我发现的一个缺点是,由于阅读器很懒,它不提供记录计数,因此,如果您需要知道总数(例如显示正确的进度信息),那么您需要使用另一个阅读器来计算行数。

      您还可以使用CSVReaderHeaderAwareBuilder

      【讨论】:

        【解决方案4】:

        我的任务是从现有 csv 中删除几列,例如 csv:

        FirstName, LastName, City, County, Zip
        Steve,Hopkins,London,Greater London,15554
        James,Bond,Vilnius,Vilniaus,03250
        

        我只需要包含值的 FirstName 和 LastName 列,并且顺序应该相同非常重要 - 默认 rd.readMap() 不保留顺序,此任务的代码:

                String[] COLUMN_NAMES_TO_REMOVE = new String[]{"", "City", "County", "Zip"};
                CSVReaderHeaderAware rd = new CSVReaderHeaderAware(new StringReader(old.csv));
                CSVWriter writer = new CSVWriter((new FileWriter(new.csv)),
                        CSVWriter.DEFAULT_SEPARATOR, CSVWriter.NO_QUOTE_CHARACTER, CSVWriter.NO_ESCAPE_CHARACTER, CSVWriter.DEFAULT_LINE_END);
        
                // let's get private field
                Field privateField = CSVReaderHeaderAware.class.getDeclaredField("headerIndex");
                privateField.setAccessible(true);
                Map<String, Integer> headerIndex = (Map<String, Integer>) privateField.get(rd);
        
                // do ordering in natural order - 0, 1, 2 ... n
                Map<String, Integer> sortedInNaturalOrder = headerIndex.entrySet().stream()
                        .sorted(Map.Entry.comparingByValue(Comparator.naturalOrder()))
                        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                                (oldValue, newValue) -> oldValue, LinkedHashMap::new));
        
                // let's get headers in natural order
                List<String> headers = sortedInNaturalOrder.keySet().stream().distinct().collect(Collectors.toList());
        
                // let's remove headers
                List<String> removedColumns = new ArrayList<String>(Arrays.asList(COLUMN_NAMES_TO_REMOVE));
                headers.removeAll(removedColumns);
                // save column names           
                writer.writeNext(headers.toArray(new String[headers.size()]));
           
                List<String> keys = new ArrayList<>();
                Map<String, String> values;
                while ((values = rd.readMap()) != null) {
                    for (String key : headers) {
                        keys.add(values.get(key));
                        if (keys.size() == headers.size()) {
                            String[] itemsArray = new String[headers.size()];
                            itemsArray = keys.toArray(itemsArray);
                            // save values                       
                            writer.writeNext(itemsArray);
                            keys.clear();
                        }
                    }
                }
                writer.flush();
        

        输出:

        FirstName, LastName
        Steve,Hopkins
        James,Bond
        

        【讨论】:

          【解决方案5】:

          看着javadoc

          如果你创建了一个 CSVReader 对象,那么你可以使用方法 .readAll 来拉取整个文件。它返回一个 String[] 列表,每个 String[] 代表文件的一行。所以现在你有了每一行的标记,你只想要第二个元素,所以把它们分开,因为它们已经很好地用分隔符给你了。在每一行你只需要第二个元素,所以:

          public static void main(String[] args){
              String data = "63,Graham,101153.06,Abraham";
              String result[] = data.split(",");
              System.out.print(result[1]);
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-12-09
            • 2020-08-11
            • 1970-01-01
            • 1970-01-01
            • 2016-06-06
            • 2016-09-06
            相关资源
            最近更新 更多