使用Java / Python从大型结构化文件中提取数据

时间:2010-09-29 19:34:12

标签: java python performance file

我有一个需要解析以提取信息的大文本文件(~100MB)。我想找到一种有效的方法。该文件以块结构构成:

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 :(得分: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

答案 1 :(得分:0)

IMO这些数据结构合理,不需要外部包来处理它。编写解析器可能不会花费几分钟的时间。它运行得非常快。

答案 2 :(得分:0)

与其引入额外的库依赖关系,并使用新库获得学习曲线,只是编写vanilla代码似乎更有效。我的算法看起来像这样(使用快速和草率的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行代码中所要求的内容(删除注释)而不是外部库依赖项。

答案 3 :(得分: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();
}

答案 4 :(得分: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

正如您在箭头中看到的那样,每条记录现在距离“====&gt;”一个街区箭头到下一个(通过将记录分隔符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
相关问题