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

使用Java/Python从大型结构化文件中提取数据,java,python,performance,file,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 = Val

我有一个大的文本文件(~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可能是一种可能性,但是这个工具将整个文件加载到内存中,这是不好的


谢谢

通常,我们会这样做。
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

IMO这种数据结构非常好,因此不需要外部包来处理它。为它编写解析器可能不会超过几分钟。它将运行得非常快。

与其产生额外的库依赖性,并利用新库加快学习速度,不如编写普通代码。我的算法看起来是这样的(使用快速而草率的Java):


当然,您必须充实“MyDataObject”类缺少的功能。但是,这基本上可以在大约20行代码(去掉注释)中满足您的要求,而不是外部库依赖项。

因为这是一种自定义格式,所以可能没有可用的库。所以你自己写一个

这里有一个启动示例,假设文件格式与您在问题中发布的一致。您可能只想使用
列表

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) {}
}
Map blocks=newlinkedhashmap();
SimpleDataFormat sdf=新的SimpleDataFormat(“EEE,dd-MMM-yyy-HH:mm:ss”,Locale.ENGLISH);
BufferedReader reader=null;
试一试{
reader=新的BufferedReader(新的InputStreamReader(新文件InputStream(“/input.txt”),“UTF-8”);
日期=空;
图块=空;
for(字符串行;(line=reader.readLine())!=null;){
line=line.trim();
如果(日期==null){
日期=sdf.parse(行);
block=新建LinkedHashMap();
区块。放置(日期,区块);
}如果(!line.isEmpty()),则为else{
String[]parts=line.split(\\s*=\\s*);
block.put(部分[0],部分[1]);
}否则{
日期=空;
}
}
}最后{
如果(reader!=null),请尝试{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();
}
for(条目块:blocks.entrySet()){
System.out.println(block.getKey());
对于(条目标记:block.getValue().entrySet()){
System.out.println(“\t”+token.getKey()+”=“+token.getValue());
}
System.out.println();
}

要高效解析文件,尤其是在大文件上,可以使用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

$awk-vRS=-vFS=“\n”{for(i=1;i您是要全部加载还是按日期/令牌选择?您将如何处理解析后的数据?哪些信息?您可以逐行迭代并返回每一行,但这将有助于了解您想要从中得到什么。所需的信息将存储在内部结构中。例如,哈希映射。它的存储位置红色不是问题。它是否来自文件中?使用异常控制流对性能和效率有巨大影响。没错……因此我的“快速草率Java”限定词!诚实的回答是,我会用Perl来做这件事……但在指定的两种语言中,Java是我更熟悉的语言。即使如此,这里的上下文听起来像是一个cron作业或手动过程,而不是响应HTTP请求或疯狂的事情……所以不要认为这么小的东西会是一个熊但是,如果你有一个不使用异常的替代实现,我很想看看。为什么注释都是大写的?看起来你在对代码的维护者大喊大叫。也许人们喜欢这样。这看起来有点难读。耸耸肩……这种注释风格在Java wo中相当常见rld。我们中的许多人在每个类、变量或方法的开头使用普通文本作为大型javadoc格式的注释,并在方法中嵌入的简短注释使用所有大写字母。这个代码块中的注释比普通代码块多得多,但这是因为编写它除了向某些人解释一个想法之外没有其他原因在你的例子中,为什么使用空格和缩进来分隔代码块,而不是大括号?我发现这很难理解。:)@史蒂夫·帕金斯:你使用了很多漂亮的空格和缩进。你怎么能说当你费尽心机确保它是非常优雅的缩进时,它会使阅读变得更困难呢?我只是问一下所有的大写字母,因为我以前没有见过。你的细节是错的-你是在呼叫组()在它上面,但是正则表达式中没有组。另外,您正在匹配文本字符2-我认为这是一个打字错误。这是我的版本:
detail\u pat=re.compile((r“\s+(\s+)\s*=\s*(\s*)”
。现在是groups()将返回名称和值的2元组。@Dave Kirby:与问题的模糊性一致,很难知道哪些组是重要的。你猜得很好,但问题完全不清楚。谢谢,我将使用Awk。我在这里找到了一篇有趣的文章:
$ awk -vRS= -vFS="\n" '{for(i=1;i<=NF;i++) if ($i ~/Token1/){ print $i} }' file
  Token1 = ValueXYZ
  Token1 = ValueXYZ
  Token1 = ValueXYZ