Как посчитать совпадающие блоки в текстовом файле Groovy - PullRequest
1 голос
/ 02 декабря 2011

Допустим, у меня есть текстовый файл, который выглядит следующим образом (включая часть filename matches):

filename    matches
bugs.txt    5
bugs.txt    3
bugs.txt    12
fish.txt    4
fish.txt    67
birds.txt    34

и т.д ...

Я хочу создать новый текстовый файл, каждая строка которого представляет собой одно имя файла со следующей информацией: filename, number of times filename appears, sum of matches

поэтому первые три строки будут читать:

bugs.txt    3    20
fish.txt    2    71
birds.txt   1    34

первая строка оригинального текстового файла (который содержит текст filename /t matches, затрудняет мне работу. Любой совет?

Вот мой код, который не совсем справляется (с одной ошибкой ...):

h = null
instances = 0
matches = 0

f.eachLine { line ->

String[] data = line.split (/\t/)

if (line =~ /filename.*/) {}

else {
    source = data[0]  

    if ( source == h) {
        instances ++
        matches = matches + data[9]
    }
    else {
        println h + '\t' + instances + '\t' + matches
        instances = 0   
        matches = 0
        h = source
    }    
} 
}

примечание: индексы для данных [] соответствуют текстовому файлу, который я использую

Ответы [ 2 ]

2 голосов
/ 02 декабря 2011

Основные проблемы с вашим кодом:

  • вы используете data[9], когда совпадения в столбце 1
  • вы пропускаете обновления и совпадения, когда source == h
  • , поскольку при изменении имени файла вы только println, вы не выводите результаты для последнего файла

Вот более простая реализация, которая накапливает результаты на карте:

// this will store a map of filename -> list of matches
// e.g. ['bugs.txt': [5, 3, 12], ...]
def fileMatches = [:].withDefault{[]}

new File('file.txt').eachLine { line ->
    // skip the header line
    if (!(line =~ /filename.*/)) {
        def (source, matches) = line.split (/\t/)
        // append number of matches source's list
        fileMatches[source] << (matches as int)
    }
}
fileMatches.each { source, matches ->
    println "$source\t${matches.size()}\t${matches.sum()}"
}
2 голосов
/ 02 декабря 2011

Я придумал это (используя фиктивные данные)

// In reality, you can get this with:
// def text = new File( 'file.txt' ).text
def text = '''filename\tmatches
             |bugs.txt\t5
             |bugs.txt\t3
             |bugs.txt\t12
             |fish.txt\t4
             |fish.txt\t67
             |birds.txt\t34'''.stripMargin()

text.split( /\n|\r|\n\r|\r\n/ ).                                // split based on newline
     drop(1)*.                                                  // drop the header line
     split( /\t/ ).                                             // then split each of these by tab
     collect { [ it[ 0 ], it[ 1 ] as int ] }.                   // convert the second element to int
     groupBy { it[ 0 ] }.                                       // group into a map by filename
     collect { k, v -> [ k, v.size(), v*.getAt( 1 ).sum() ] }*. // then make a list of file,nfiles,sum
     join( '\t' ).                                              // join each of these into a string separated by tab
     each {                                                     // then print them out
       println it
     }

Очевидно, что все файлы загружаются в память за один раз ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...