Извлечение данных из большого структурированного файла с использованием Java / Python - PullRequest
0 голосов
/ 29 сентября 2010

У меня большой текстовый файл (~ 100 МБ), который необходимо проанализировать для извлечения информации. Я хотел бы найти эффективный способ сделать это. Файл структурирован в блоке:

Mon, 01 Jan 2010 01:01:01
  Token1 = ValueXYZ
  Token2 = ValueABC
  Token3 = ValuePQR
  ...
  TokenX = Value123

Mon, 01 Jan 2010 01:02:01
  Token1 = ValueXYZ
  Token2 = ValueABC
  Token3 = ValuePQR
  ...
  TokenY = Value456

Есть ли библиотека, которая может помочь в анализе этого файла? (В Java, Python, любой инструмент командной строки)

Редактировать: Я знаю, что вопрос расплывчатый, но ключевым элементом является не способ прочитать файл, разобрать его с помощью регулярных выражений и т. Д. сроки исполнения. Например, Antlr мог бы быть возможностью, но этот инструмент загружает весь файл в память, что нехорошо.

Спасибо!

Ответы [ 5 ]

0 голосов
/ 30 сентября 2010

Для эффективного анализа файлов, особенно больших файлов, вы можете использовать awk. Пример

$ awk -vRS= '{print "====>" $0}' file
====>Mon, 01 Jan 2010 01:01:01
  Token1 = ValueXYZ
  Token2 = ValueABC
  Token3 = ValuePQR
  ...
  TokenX = Value123
====>Mon, 01 Jan 2010 01:02:01
  Token1 = ValueXYZ
  Token2 = ValueABC
  Token3 = ValuePQR
  ...
  TokenY = Value456
====>Mon, 01 Jan 2010 01:03:01
  Token1 = ValueXYZ
  Token2 = ValueABC
  Token3 = ValuePQR

Как вы можете видеть со стрелками, каждая запись теперь является одним блоком от стрелок "====>" до следующей (путем установки разделителя записей RS на пробелы). затем вы можете установить разделитель полей, например, новую строку

$ awk -vRS= -vFS="\n" '{print "====>" $1}' file
====>Mon, 01 Jan 2010 01:01:01
====>Mon, 01 Jan 2010 01:02:01
====>Mon, 01 Jan 2010 01:03:01

Итак, в приведенном выше примере каждое 1-е поле является отметкой даты / времени. Например, чтобы получить «token1», вы можете сделать это

$ awk -vRS= -vFS="\n" '{for(i=1;i<=NF;i++) if ($i ~/Token1/){ print $i} }' file
  Token1 = ValueXYZ
  Token1 = ValueXYZ
  Token1 = ValueXYZ
0 голосов
/ 30 сентября 2010

Поскольку это пользовательский формат, скорее всего, нет доступной библиотеки. Так что пиши себе.

Вот начальный пример, предполагая, что формат файла является подходящим, как вы разместили в вопросе. Вместо этого вы можете использовать List<Block>:

Map<Date, Map<String, String>> blocks = new LinkedHashMap<Date, Map<String, String>>();
SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss", Locale.ENGLISH);
BufferedReader reader = null;

try {
    reader = new BufferedReader(new InputStreamReader(new FileInputStream("/input.txt"), "UTF-8"));
    Date date = null;
    Map<String, String> block = null;

    for (String line; (line = reader.readLine()) != null;) {
        line = line.trim();
        if (date == null) {
            date = sdf.parse(line);
            block = new LinkedHashMap<String, String>();
            blocks.put(date, block);
        } else if (!line.isEmpty()) {
            String[] parts = line.split("\\s*=\\s*");
            block.put(parts[0], parts[1]);
        } else {
            date = null;
        }
    }
} finally {
    if (reader != null) try { reader.close(); } catch (IOException ignore) {}
}

Чтобы проверить содержимое, используйте это:

for (Entry<Date, Map<String, String>> block : blocks.entrySet()) {
    System.out.println(block.getKey());
    for (Entry<String, String> token : block.getValue().entrySet()) {
        System.out.println("\t" + token.getKey() + " = " + token.getValue());
    }
    System.out.println();
}
0 голосов
/ 30 сентября 2010

IMO эти данные настолько хорошо структурированы, что внешний пакет для их обработки не нужен.Вероятно, на написание парсера для него уйдет не более нескольких минут.Это будет работать довольно быстро.

0 голосов
/ 30 сентября 2010

Вместо того, чтобы вызывать дополнительную библиотечную зависимость и поднимать кривую обучения с этой новой библиотекой, было бы более эффективно просто писать ванильный код.Мой алгоритм выглядел бы примерно так (используя быструю и небрежную Java):


// HOLDER FOR ALL THE DATA OBJECT THAT ARE EXTRACTED FROM THE FILE
ArrayList allDataObjects = new ArrayList();
// BUFFER FOR THE CURRENT DATA OBJECT BEING EXTRACTED
MyDataObject workingObject = null;
// BUILT-IN JAVA PARSER TO HELP US DETERMINE WHETHER OR NOT A LINE REPRESENTS A DATE
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss");

// PARSE THROUGH THE FILE LINE-BY-LINE
BufferedReader inputFile = new BufferedReader(new FileReader(new File("myFile.txt")));
String currentLine = "";
while((currentLine = inputFile.readLine()) != null)
{
    try
    {
        // CHECK WHETHER OR NOT THE CURRENT LINE IS A DATE
        Date parsedDate = dateFormat.parse(currentLine.trim());
    }
    catch(ParseException pe)
    {
        // THE CURRENT LINE IS NOT A DATE.  THAT MEANS WE'RE 
        // STILL PULLING IN TOKENS FOR THE LAST DATA OBJECT.
        workingObject.parseAndAddToken(currentLine);
        continue;
    }
    // THE ONLY WAY WE REACH THIS CODE IS IF THE CURRENT LINE
    // REPRESENTS A DATE, WHICH MEANS WE'RE STARTING ON A NEW
    // DATA OBJECT.  ADD THE LAST DATA OBJECT TO THE LIST,
    // AND START UP A NEW WORKING DATA OBJECT.
    if(workingObject != null) allDataObjects.add(workingObject);
    workingObject = new MyDataObject();
    workingObject.parseAndSetDate(currentLine);
}
inputFile.close();
// NOW YOU'RE READY TO DO WHATEVER WITH "allDataObjects"

Конечно, вам пришлось бы выделить недостающие функциональные возможности для класса "MyDataObject".Тем не менее, это в основном делает то, что вы просите, примерно в 20 или около того строках кода (без комментариев), а не во внешних библиотечных зависимостях.

0 голосов
/ 30 сентября 2010

Обычно мы делаем что-то подобное.Библиотека re в значительной степени справляется с этим.Использование функции генератора управляет вложенной структурой.

def gen_blocks( my_file ):
    header_pat= re.compile( r"\w3, \d2 \w3 \d4 \d2:\d2:\d2" )
    detail_pat = re.compile( r"\s2\S*\s+=\s+\S*" )
    lines = []
    for line in my_file:
        hdr_match=header_pat.match( line )
        if hdr_match:
            if lines:
                yield header, lines
                lines= []
             header= hdr.match.groups()
             continue
         dtl_match= detail_pat.match( line )
         if dtl_match:
             lines.append( dtl_match.groups() )
             continue
         # Neither kind of line, maybe blank or maybe an error
     if lines:
         yield header, lines

for header, lines in gen_blocks( some_file ):
    print header, lines
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...