如何在Java中验证两个数据相同但列顺序不同的CSV文件?

rhfm7lfc  于 2022-12-06  发布在  Java
关注(0)|答案(1)|浏览(140)

我尝试比较两个CSV文件,这两个文件具有相同的数据,但列的顺序不同。当列顺序匹配时,下面的代码可以正常工作:当列顺序在CSV文件之间不匹配时,我如何调整下面的代码使其正常工作?

Set<String> source = new HashSet<>(org.apache.commons.io.FileUtils.readLines(new File(sourceFile)));
        Set<String> target = new HashSet<>(org.apache.commons.io.FileUtils.readLines(new File(targetFile)));
        return source.containsAll(target) && target.containsAll(source)

例如,上面的测试通过时,源文件和目标文件是这样的:
源文件:

a,b,c
1,2,3
4,5,6

目标文件:

a,b,c
1,2,3
4,5,6

但是,源文件是相同的,但如果目标文件是以下面的方式,它就不起作用了。
目标文件:

a,c,b
1,3,2
4,6,5
busg9geu

busg9geu1#

下面是一些可以工作的代码。它依赖于包含列标题的每个文件的第一行。
不过,这不仅仅是一种调整,而是一种“老狗”方法。
问题中的原始代码包含以下行:

Set<String> source = new HashSet<>(org.apache.commons.io.FileUtils.readLines(new File(sourceFile)));
Set<String> target = new HashSet<>(org.apache.commons.io.FileUtils.readLines(new File(targetFile)));

使用此解决方案,传入的数据需要进行更多处理,然后才能放入Set

List<String> source = (org.apache.commons.io.FileUtils.readLines(new File(sourceFile)));
List<String> target = (org.apache.commons.io.FileUtils.readLines(new File(targetFile)));

这种方法将比较目标文件和源文件中的列标题,并使用该结果生成一个int []来指示列顺序的差异。
填充顺序差数组后,文件中的数据将被放入一对Set<List<String>>中,每个List<String>将代表源数据文件和目标数据文件中的一行,List中的每个String将代表一列数据。
在下面的代码中,main是测试驱动程序。仅出于测试目的,数据文件已被替换为一对String [],而用org.apache.commons.io.FileUtils.readLines阅读文件已被替换为Arrays.asList

package comparecsv;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class CompareCSV {

    private static int [] columnReorder;

    private static void headersOrder
                              (String sourceHeader, String targetHeader) {
            String [] columnHeader = sourceHeader.split (",");
            List<String> sourceColumn = Arrays.asList (columnHeader);
            columnReorder = new int [columnHeader.length];
            String [] targetColumn = targetHeader.split (",");
            for (int i = 0; i < targetColumn.length; ++i) {
                int j = sourceColumn.indexOf(targetColumn[i]);
                columnReorder [i] = j;
            }
    }

    private static Set<List<String>> toSet
                               (List<String> data, boolean reorder) {
        Set<List<String>> dataSet = new HashSet<> ();
        for (String s: data)  {
            String [] byColumn = s.split (",");
            if (reorder) {
                String [] reordered = new String [byColumn.length];
                for (int i = 0; i < byColumn.length; ++i) {
                   reordered[columnReorder[i]] = byColumn [i];
                }
                dataSet.add (Arrays.asList (reordered));
            } else {
                dataSet.add (Arrays.asList(byColumn));
            }
        }
        return dataSet;
    }

    public static void main(String[] args) {
        String [] sourceData = {"a,b,c,d,e", "1,2,3,4,5", "6,7,8,9,10"
            ,"11,12,13,14,15", "16,17,18,19,20"};
        String [] targetData = {"c,b,e,d,a", "3,2,5,4,1", "8,7,10,9,6"
            ,"13,12,15,14,11", "18,17,20,19,16"};
        List<String> source = Arrays.asList(sourceData);
        List<String> target = Arrays.asList (targetData);

        headersOrder (source.get(0), target.get(0));
        Set<List<String>> sourceSet = toSet (source, false);
        Set<List<String>> targetSet = toSet (target, true);
        System.out.println ( sourceSet.containsAll (targetSet)
                + "  " + targetSet.containsAll (sourceSet) + "  " +
                   (    sourceSet.containsAll (targetSet)
                     &&  targetSet.containsAll (sourceSet)));
    }
}

headersOrder方法逐列比较标题,并填充columnReorder数组。toSet方法创建Set<List<String>>,根据boolean参数的值对列重新排序或不重新排序。
为简化起见,这里假设行可以很容易地用逗号分割。诸如dog, "Reginald, III", 3之类的数据将导致失败。
在测试中,我发现文件中的行可以与另一个文件中的对应行匹配,而不管行的顺序如何。

Source:
a,b,c
1,2,3
4,5,6
7,8,9

Target:
a,b,c
4,5,6
7,8,9
1,2,3

结果将是内容匹配。
我相信这会与O/P问题代码的结果相匹配。但是,为了使此解决方案有效,每个文件的第一行 * 必须 * 包含列标题。

相关问题