【问题标题】:I need to take a CSV file and split it into separate files based on Column Header [JAVA]我需要获取一个 CSV 文件并根据列标题 [JAVA] 将其拆分为单独的文件
【发布时间】:2020-08-10 18:33:55
【问题描述】:

我对 Java 还很陌生,正在努力阅读 > 排序 > 导出 csv。我有一个带有 [X, Y, Z, Scalar 1, Scalar 2, Scalar 3, Scalar 4] 的 csv 作为需要分成 4 个 csv 的标题。实际文件长达数千行,示例如下:

[X,Y,Z, Sc1, Sc2, Sc3, Sc4]
[1,0,0,   5,   7,   9,  10]
[0,1,1,   6,   8,   4,   0]
[0,0,1,   3,   3,   8,   2]

我需要将源 csv 拆分为 4 个单独的 csv,其中包含一个标量值和 x、y、z 数据。

File 1       | File 2       | File 3       | File 4
----------------------------------------------------------
[Sc1, X,Y,Z] | [Sc2, X,Y,Z] | [Sc3, X,Y,Z] | [Sc4,  X,Y,Z]
[5,   1,0,0] | [7,   1,0,0] | [9,   1,0,0] | [10,   1,0,0]
[6,   0,1,1] | [8,   0,1,1] | [4,   0,1,1] | [ 0,   0,1,1]
[3,   0,0,1] | [3,   0,0,1] | [8,   0,0,1] | [ 2,   0,0,1]

我目前正在使用 BufferedReader 读取数据,但我不确定一旦读取数据后如何组织数据,或者这是否是一个好方法。

 ArrayList<String> readFileFast (String expDir,String filename) {
        String path = expDir + filename;
        ArrayList<String> fileContents = new ArrayList<>();
        try {
            BufferedReader br = new BufferedReader(new FileReader(path));
            String line;
            while ((line = br.readLine()) != null) {
                fileContents.add(line);
            }
        } catch (Exception e) {
            SuperStackPrint(e);
        }
        return fileContents;
      }

println(readFileFast(expDir, "/DELETEME.csv"));

任何有关如何正确执行此操作的见解将不胜感激。

【问题讨论】:

    标签: java csv sorting export-to-csv


    【解决方案1】:

    您将受益于使用专门读写 CSV 文件的库。有几个可供选择,但这里我将使用OpenCSV

    如果你最终没有使用这个库,它至少可以为你提供一些想法来实现你自己的方法。

    此外,在使用库时,我建议使用 Maven 或 Gradle 等工具来帮助管理此问题,因为这些工具会为您处理“依赖项的依赖关系” - 例如,OpenCSV 库本身需要访问其他它使用的库。

    对于 Maven,这里是我的 POM 文件的 OpenCSV 依赖项:

    <dependency>
        <groupId>com.opencsv</groupId>
        <artifactId>opencsv</artifactId>
        <version>5.2</version>
    </dependency>
    

    方法:

    1. 创建一个 Java 类(一个“bean”)来保存将从 CSV 源文件加载的数据。在我的示例中,这将被称为 SplitBean

    2. 使用此类创建对象集合,其中每个对象都包含 CSV 文件的一行数据

    3. 遍历这个对象集合,将相关部分写入 4 个输出文件。

    您可以选择在不使用 OpenCSV 或类似库的情况下遵循上述方法。但是您必须自己编写更多与基本 CSV 操作相关的代码。在你的情况下,数据并不复杂,所以这不是不合理的。

    无论哪种方式,我建议创建一个类来表示一行输入数据,然后在写入输出文件时处理这些对象的列表。这将流程分成 2 个不同的步骤,并利用 Java 对象来简化流程。

    这是SplitBean 类:

    import com.opencsv.bean.CsvBindByName;
            
    public class SplitBean {
        @CsvBindByName(column = "X")
        private int x;
    
        @CsvBindByName(column = "Y")
        private int y;
    
        @CsvBindByName(column = "Z")
        private int z;
        
        @CsvBindByName(column = "Sc1")
        private int  sc1;
    
        @CsvBindByName(column = "Sc2")
        private int  sc2;
    
        @CsvBindByName(column = "Sc3")
        private int  sc3;
    
        @CsvBindByName(column = "Sc4")
        private int  sc4;
    
        public static String[] getHeadingsOne() {
            String[] s = { "Sc1", "X", "Y", "Z" };
            return s;
        }
        
        public static String[] getHeadingsTwo() {
            String[] s = { "Sc2", "X", "Y", "Z" };
            return s;
        }
        
        public static String[] getHeadingsThree() {
            String[] s = { "Sc3", "X", "Y", "Z" };
            return s;
        }
        
        public static String[] getHeadingsFour() {
            String[] s = { "Sc4", "X", "Y", "Z" };
            return s;
        }
        
        public String[] getDataOne() {
            String[] i = { String.valueOf(sc1), String.valueOf(x), 
                String.valueOf(y), String.valueOf(z) };
            return i;
        }
        
        public String[] getDataTwo() {
            String[] i = { String.valueOf(sc2), String.valueOf(x), 
                String.valueOf(y), String.valueOf(z) };
            return i;
        }
        
        public String[] getDataThree() {
            String[] i = { String.valueOf(sc3), String.valueOf(x), 
                String.valueOf(y), String.valueOf(z) };
            return i;
        }
        
        public String[] getDataFour() {
            String[] i = { String.valueOf(sc4), String.valueOf(x), 
                String.valueOf(y), String.valueOf(z) };
            return i;
        }
        
        public int getX() {
            return x;
        }
    
        public void setX(int x) {
            this.x = x;
        }
    
        public int getY() {
            return y;
        }
    
        public void setY(int y) {
            this.y = y;
        }
    
        public int getZ() {
            return z;
        }
    
        public void setZ(int z) {
            this.z = z;
        }
    
        public int getSc1() {
            return sc1;
        }
    
        public void setSc1(int sc1) {
            this.sc1 = sc1;
        }
    
        public int getSc2() {
            return sc2;
        }
    
        public void setSc2(int sc2) {
            this.sc2 = sc2;
        }
    
        public int getSc3() {
            return sc3;
        }
    
        public void setSc3(int sc3) {
            this.sc3 = sc3;
        }
    
        public int getSc4() {
            return sc4;
        }
    
        public void setSc4(int sc4) {
            this.sc4 = sc4;
        }
        
    }
    

    此类使用@CsvBindByName 注释将源 CSV 文件中的列标题名称映射到类本身中的字段名称。你不需要这样做,但它是 OpenCSV 提供的一个方便的功能。

    该类还包含处理 4 个不同输出文件(输入文件数据的子集)的方法。

    现在我们可以编写一个单独的doTheSplit() 方法来使用这个类:

    import com.opencsv.bean.CsvToBean;
    import com.opencsv.bean.CsvToBeanBuilder;
    import com.opencsv.bean.HeaderColumnNameMappingStrategy;
    import com.opencsv.exceptions.CsvDataTypeMismatchException;
    import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
    import com.opencsv.CSVWriter;
    import java.io.IOException;
    import java.io.Reader;
    import java.io.FileWriter;
    import java.net.URISyntaxException;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.util.List;
    
    public class SplitData {
    
        public void doTheSplit() throws URISyntaxException, IOException,
                CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
            HeaderColumnNameMappingStrategy msIn = new HeaderColumnNameMappingStrategy();
            msIn.setType(SplitBean.class);
    
            Path path = Paths.get("C:/tmp/csvsplit/input.csv");
            List<SplitBean> list;
    
            // read the data from the input CSV file into our SplitBean list:
            try ( Reader reader = Files.newBufferedReader(path)) {
                CsvToBean cb = new CsvToBeanBuilder(reader)
                        .withMappingStrategy(msIn)
                        .build();
                list = cb.parse();
                int i = 1;
            }
    
            // set up 4 file writers:
            try ( CSVWriter writer1 = new CSVWriter(new FileWriter("C:/tmp/csvsplit/output1.csv"));
                    CSVWriter writer2 = new CSVWriter(new FileWriter("C:/tmp/csvsplit/output2.csv"));
                    CSVWriter writer3 = new CSVWriter(new FileWriter("C:/tmp/csvsplit/output3.csv"));
                    CSVWriter writer4 = new CSVWriter(new FileWriter("C:/tmp/csvsplit/output4.csv"))) {
    
                // first write the headers to each file (false = no quotes):
                writer1.writeNext(SplitBean.getHeadingsOne(), false);
                writer2.writeNext(SplitBean.getHeadingsTwo(), false);
                writer3.writeNext(SplitBean.getHeadingsThree(), false);
                writer4.writeNext(SplitBean.getHeadingsFour(), false);
                
                // then write each row of data (false = no quotes):
                for (SplitBean item : list) {
                    writer1.writeNext(item.getDataOne(), false);
                    writer2.writeNext(item.getDataTwo(), false);
                    writer3.writeNext(item.getDataThree(), false);
                    writer4.writeNext(item.getDataFour(), false);
                }
            }
        }
    
    }
    

    此代码的第一部分填充List&lt;SplitBean&gt; list。输入电子表格中的每一行数据都有一个 splitBean 对象。 OpenCSV 会在幕后为您处理大部分工作。

    然后,代码创建 4 个文件编写器,它们使用 OpenCSV CSVWriter 对象,帮助将我们的数据格式化为有效的 CSV 行。

    使用此代码,我们将列标题写入 4 个文件中的每一个。最后,我们遍历我们的 SplitBean 项目集合,并将相关数据子集写入每个文件。

    因此,对于这样的 CSV 输入文件:

    X,Y,Z,Sc1,Sc2,Sc3,Sc4
    1,0,0,5,7,9,10
    0,1,1,6,8,4,0
    0,0,1,3,3,8,2
    

    我们最终得到 4 个不同的输出文件。一个例子:

    Sc1,X,Y,Z
    5,1,0,0
    6,0,1,1
    3,0,0,1
    

    附加说明:以这种方式使用SplitBean 类的一大优势是,如果您决定需要执行更多数据转换,例如过滤掉数据行,或以不同方式对数据进行排序。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-01
      • 1970-01-01
      • 2022-01-16
      • 2018-02-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多