2013-03-03 3 views
2

SuperCSV CsvBeanReader를 사용하여 매핑 및 셀 유효성 검사를 수행하는 다양한 CSV 파일을 포함하는 여러 프로젝트가 있습니다. 나는 csv 파일 당 콩을 만들고 지나쳤다. 각 bean의 equals, hashCode 및 toStringcsv에서 중복 행을 찾아보고하는 방법에 대한 제안

나는 csv 라인 중복 식별을 수행하기위한 최상의 "모든 프로젝트"구현 방법이 무엇인지에 대한 제안을 찾고 있습니다. 원본 csv 행 번호 및 행 내용뿐만 아니라 발견 된 모든 중복 행의 행 번호 및 행 내용을보고합니다 (제거 아님). 파일 중 일부는 크기가 GB를 초과하는 수십만 줄에 도달 할 수 있으며 파일 당 읽기 수를 최소화하려는 의도가 있으며 CsvBeanReader가 파일을 열어 놓은 상태에서 수행 할 수 있다고 생각했습니다.

미리 감사드립니다.

답변

2

파일의 크기와 원본 및 복제본의 라인 내용을 원한다면 파일을 2 번 통과하는 것이 가장 좋습니다.

복제본의 최신 라인 콘텐츠 만 원한다면 1 패스로 벗어날 수 있습니다. 원본의 라인 내용과 모든 복제물을 1 패스로 추적하면 모든 행의 내용을 저장해야한다는 것을 의미합니다. 아마도 메모리가 부족할 것입니다.

내 솔루션은 동일한 hashCode()의 두 콩이 중복 된 것으로 가정합니다. equals()을 사용해야하는 경우 더 복잡해집니다.

  • 패스 1 : (각 중복 해시에 대한 기록 행 번호) 중복을 식별

  • 패스 2 :

패스 1 중복에 대한 보고서 : 중복 확인

/** 
* Finds the row numbers with duplicate records (using the bean's hashCode() 
* method). The key of the returned map is the hashCode and the value is the 
* Set of duplicate row numbers for that hashcode. 
* 
* @param reader 
*   the reader 
* @param preference 
*   the preferences 
* @param beanClass 
*   the bean class 
* @param processors 
*   the cell processors 
* @return the map of duplicate rows (by hashcode) 
* @throws IOException 
*/ 
private static Map<Integer, Set<Integer>> findDuplicates(
    final Reader reader, final CsvPreference preference, 
    final Class<?> beanClass, final CellProcessor[] processors) 
    throws IOException { 

    ICsvBeanReader beanReader = null; 
    try { 
    beanReader = new CsvBeanReader(reader, preference); 

    final String[] header = beanReader.getHeader(true); 

    // the hashes of any duplicates 
    final Set<Integer> duplicateHashes = new HashSet<Integer>(); 

    // the hashes for each row 
    final Map<Integer, Set<Integer>> rowNumbersByHash = 
     new HashMap<Integer, Set<Integer>>(); 

    Object o; 
    while ((o = beanReader.read(beanClass, header, processors)) != null) { 
     final Integer hashCode = o.hashCode(); 

     // get the row no's for the hash (create if required) 
     Set<Integer> rowNumbers = rowNumbersByHash.get(hashCode); 
     if (rowNumbers == null) { 
     rowNumbers = new HashSet<Integer>(); 
     rowNumbersByHash.put(hashCode, rowNumbers); 
     } 

     // add the current row number to its hash 
     final Integer rowNumber = beanReader.getRowNumber(); 
     rowNumbers.add(rowNumber); 

     if (rowNumbers.size() == 2) { 
     duplicateHashes.add(hashCode); 
     } 

    } 

    // create a new map with just the duplicates 
    final Map<Integer, Set<Integer>> duplicateRowNumbersByHash = 
     new HashMap<Integer, Set<Integer>>(); 
    for (Integer duplicateHash : duplicateHashes) { 
     duplicateRowNumbersByHash.put(duplicateHash, 
      rowNumbersByHash.get(duplicateHash)); 
    } 

    return duplicateRowNumbersByHash; 

    } finally { 
    if (beanReader != null) { 
     beanReader.close(); 
    } 
    } 
} 

이 방법의 대안으로 CsvListReader을 사용하고 getUntokenizedRow().hashCode()을 사용할 수 있습니다. 이렇게하면 원시 CSV 문자열을 기반으로 해시가 계산됩니다 (훨씬 빨라지 겠지만 데이터에 미묘한 차이가있을 수 있음). 작업).

패스 2 : 중복

이 방법에 대한 보고서는 이전 방법의 출력을 소요하고 신속하게 중복 기록하고 중복 다른 행을 식별하는 데 사용합니다.

/** 
    * Reports the details of duplicate records. 
    * 
    * @param reader 
    *   the reader 
    * @param preference 
    *   the preferences 
    * @param beanClass 
    *   the bean class 
    * @param processors 
    *   the cell processors 
    * @param duplicateRowNumbersByHash 
    *   the row numbers of duplicate records 
    * @throws IOException 
    */ 
    private static void reportDuplicates(final Reader reader, 
     final CsvPreference preference, final Class<?> beanClass, 
     final CellProcessor[] processors, 
     final Map<Integer, Set<Integer>> duplicateRowNumbersByHash) 
     throws IOException { 

    ICsvBeanReader beanReader = null; 
    try { 
     beanReader = new CsvBeanReader(reader, preference); 

     final String[] header = beanReader.getHeader(true); 

     Object o; 
     while ((o = beanReader.read(beanClass, header, processors)) != null) { 
     final Set<Integer> duplicateRowNumbers = 
      duplicateRowNumbersByHash.get(o.hashCode()); 
     if (duplicateRowNumbers != null) { 
      System.out.println(String.format(
      "row %d is a duplicate of rows %s, line content: %s", 
      beanReader.getRowNumber(), 
      duplicateRowNumbers, 
      beanReader.getUntokenizedRow())); 
     } 

     } 

    } finally { 
     if (beanReader != null) { 
     beanReader.close(); 
     } 
    } 
    } 

샘플

다음 2 가지 방법이 사용되는 방법의 예이다.

// rows (2,4,8) and (3,7) are duplicates 
    private static final String CSV = "a,b,c\n" + "1,two,01/02/2013\n" 
     + "2,two,01/02/2013\n" + "1,two,01/02/2013\n" 
     + "3,three,01/02/2013\n" + "4,four,01/02/2013\n" 
     + "2,two,01/02/2013\n" + "1,two,01/02/2013\n"; 

    private static final CellProcessor[] PROCESSORS = { new ParseInt(), 
     new NotNull(), new ParseDate("dd/MM/yyyy") }; 

    public static void main(String[] args) throws IOException { 

    final Map<Integer, Set<Integer>> duplicateRowNumbersByHash = findDuplicates(
     new StringReader(CSV), CsvPreference.STANDARD_PREFERENCE, 
     Bean.class, PROCESSORS); 

    reportDuplicates(new StringReader(CSV), 
     CsvPreference.STANDARD_PREFERENCE, Bean.class, PROCESSORS, 
     duplicateRowNumbersByHash); 

    } 

출력 :

row 2 is a duplicate of rows [2, 4, 8], line content: 1,two,01/02/2013 
row 3 is a duplicate of rows [3, 7], line content: 2,two,01/02/2013 
row 4 is a duplicate of rows [2, 4, 8], line content: 1,two,01/02/2013 
row 7 is a duplicate of rows [3, 7], line content: 2,two,01/02/2013 
row 8 is a duplicate of rows [2, 4, 8], line content: 1,two,01/02/2013 
관련 문제