2010-09-29 4 views
0

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

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, любой инструмент командной строки)

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

Спасибо!

+2

Вы собираетесь загрузить их все или выбрать и выбрать по дате/значку? И что вы будете делать с анализируемыми данными? –

+0

Какая информация? Вы можете прокручивать его по строкам и прерывать каждую строку, но это поможет узнать, чего вы хотите от нее. – snapshoe

+0

Необходимая информация будет храниться во внутренней структуре. Например, хэш-карта. – legege

ответ

0

Для эффективного разбора файлов, особенно на большой файл, вы можете использовать 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 

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

$ awk -vRS= -vFS="\n" '{for(i=1;i<=NF;i++) if ($i ~/Token1/){ print $i} }' file 
    Token1 = ValueXYZ 
    Token1 = ValueXYZ 
    Token1 = ValueXYZ 
+0

Спасибо, я пойду с Awk. Я нашел интересную статью здесь: http://www.ibm.com/developerworks/library/l-awk2.html – legege

0

Обычно мы делаем что-то подобное. Библиотека 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 
+0

Неправильно задано регулярное выражение detail_pat - вы вызываете на нем группы(), но в регулярном выражении нет групп. Также вы соответствуете буквенному символу 2 - опечатку, я думаю. Вот моя версия: 'detail_pat = re.compile ((r" \ s + (\ S +) \ s * = \ s * (\ S *) ")'. Теперь group() вернет 2-кортеж имени и значение. –

+0

@Dave Kirby: В соответствии с неопределенностью проблемы трудно понять, какие группы имеют значение. Вы хорошо поняли, но вопрос совершенно неясен. –

0

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

0

Вместо того, чтобы нести дополнительную зависимость от библиотеки и получать кривую обучения с помощью этой новой библиотеки, было бы более эффективно писать код ванили. Мой алгоритм будет выглядеть следующим образом (используя быстрый и неаккуратный 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

Использование исключений для управления потоком * * Влияние на производительность и эффективность. – BalusC

+0

Истинный ... отсюда мой «быстрый и ненадежный Java» квалификатор! Честный ответ заключается в том, что я сделал бы это в Perl ... но из двух указанных языков, Java - это тот, с которым я Да, более знакомы. Несмотря на это, похоже, что контекст здесь - это задание cron или ручной процесс, а не что-то, отвечающее на HTTP-запросы или что-то сумасшедшее ... так что не думайте, что этот крошечный был бы медведем в контексте. Однако, если у вас есть альтернативная реализация, которая не используйте исключения, мне было бы интересно увидеть это. –

+0

ПОЧЕМУ КОММЕНТАРИЙ В ВСЕХ КАПС? ЭТО СМОТРЕТЬ, КАК ВЫ СМОТРИТЕ В ОСНОВЕ ЭТОГО КОДА. Может быть, людям это нравится. Кажется, немного труднее читать. –

0

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

Вот пример запуска, предполагая, что формат файла является последовательным, когда вы отправляли в вопрос. Вы только можете использовать 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(); 
} 
Смежные вопросы