我有一个需要解析以提取信息的大文本文件(~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可能是一种可能,但是这个工具将整个文件加载到内存中,这是不好的。
谢谢!
答案 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