分析包含JSON和文本结构的txt文件中的JSON结构



我有一个带有json结构的txt文件。问题是该文件不仅包含json结构,还包含原始文本,如日志错误:

2019-01-18 21:00:05.4521|INFO|Technical|Batch Started|
2019-01-18 21:00:08.8740|INFO|Technical|Got Entities List from 20160101 00:00 : 
{
"name": "1111",
"results": [{
"filename": "xxxx",
"numberID": "7412"
}, {
"filename": "xgjhh",
"numberID": "E52"
}]
}
2019-01-18 21:00:05.4521|INFO|Technical|Batch Started|
2019-01-18 21:00:08.8740|INFO|Technical|Got Entities List from 20160101 00:00 :
{
"name": "jfkjgjkf",
"results": [{
"filename": "hhhhh",
"numberID": "478962"
}, {
"filename": "jkhgfc",
"number": "12544"
}]
}

我读了.txt文件,但试图修补jason结构时遇到了一个错误:在:

import json
with open("data.txt", "r", encoding="utf-8", errors='ignore') as f:
json_data = json.load(f)

OUT:json.decoder.JSONDecodeError:额外数据:第1行第5列(字符4)

我想parce json并保存为csv文件。

在不假设非JSON内容的情况下,解析一个JSON对象与其他内容混合的文件的更通用的解决方案是通过花括号将文件内容拆分为多个片段,从第一个开头的花括号片段开始,然后逐个连接其余片段,直到连接的字符串可解析为JSON:

import re
fragments = iter(re.split('([{}])', f.read()))
while True:
try:
while True:
candidate = next(fragments)
if candidate == '{':
break
while True:
candidate += next(fragments)
try:
print(json.loads(candidate))
break
except json.decoder.JSONDecodeError:
pass
except StopIteration:
break

该输出:

{'name': '1111', 'results': [{'filename': 'xxxx', 'numberID': '7412'}, {'filename': 'xgjhh', 'numberID': 'E52'}]}
{'name': 'jfkjgjkf', 'results': [{'filename': 'hhhhh', 'numberID': '478962'}, {'filename': 'jkhgfc', 'number': '12544'}]}

此解决方案将剥离非JSON结构,并将其封装在包含JSON结构中。这应该能帮你完成任务。我发布这篇文章是为了权宜之计,然后我会编辑我的答案,以获得更清晰的解释。当我完成后,我会编辑第一个部分:

import json
with open("data.txt", "r", encoding="utf-8", errors='ignore') as f:
cleaned = ''.join([item.strip() if item.strip() is not '' else '-split_here-' for item in f.readlines() if '|INFO|' not in item]).split('-split_here-')
json_data = json.loads(json.dumps(('{"entries":[' + ''.join([entry + ', ' for entry in cleaned])[:-2] + ']}')))

输出:

{"entries":[{"name": "1111","results": [{"filename": "xxxx","numberID": "7412"}, {"filename": "xgjhh","numberID": "E52"}]}, {"name": "jfkjgjkf","results": [{"filename": "hhhhh","numberID": "478962"}, {"filename": "jkhgfc","number": "12544"}]}]}

这是怎么回事?

cleaned = ...行中,我们使用一个list comprehension,它创建文件(f.readlines())中不包含字符串|INFO|的行的list,并在有空行时将字符串-split_here-添加到列表中(其中.strip()产生'')。

然后,我们将行(''.join())的list转换为string

最后,我们将该字符串(.split('-split_here-')转换为列表的list,将JSON结构分离为它们自己的list,在data.txt中用空行标记。

json_data = ...行中,我们使用列表理解为每个JSON结构添加一个","。

然后,我们将该list转换回单个string,剥离最后一个', '(字符串中最后两个字符的.join()[:-2].[:-2]切片)

然后,我们用'{"entries":['']}'包装字符串,使整个字符串成为有效的JSON结构,并将其提供给json.dumpsjson.loads,以清除任何编码,并将数据加载为python对象。

您可以做以下几件事之一:

  • 在命令行上,删除所有出现"|INFO|Technical|"的行(假设它出现在原始文本的每一行中):
    sed -i '' -e '/|INFO|Technical/d' yourfilename(如果在Mac上),
    sed -i '/|INFO|Technical/d' yourfilename(如果在Linux上)。

  • 将这些原始行移动到它们自己的JSON字段中

使用"文本结构"作为JSON对象之间的分隔符。

遍历文件中的行,将它们保存到缓冲区,直到遇到一行文本行,然后解析保存为JSON对象的行。

import re
import json
def is_text(line):
# returns True if line starts with a date and time in "YYYY-MM-DD HH:MM:SS" format
line = line.lstrip('|') # you said some lines start with a leading |, remove it
return re.match("^(d{4})-(d{2})-(d{2}) (d{2}):(d{2}):(d{2})", line)
json_objects = []
with open("data.txt") as f:
json_lines = []
for line in f:
if not is_text(line):
json_lines.append(line)
else:
# if there's multiple text lines in a row json_lines will be empty
if json_lines:
json_objects.append(json.loads("".join(json_lines)))
json_lines = []
# we still need to parse the remaining object in json_lines
# if the file doesn't end in a text line
if json_lines:
json_objects.append(json.loads("".join(json_lines)))
print(json_objects)

在最后两行中重复逻辑有点难看,但您需要处理文件中最后一行不是文本行的情况,因此当您完成for循环时,您需要解析json_lines中的最后一个对象(如果有)。

我假设文本行之间永远不会有一个以上的JSON对象,而且我的日期正则表达式将在8000年后中断。

您可以计算文件中的花括号来查找json的开头和结尾,并将它们存储在列表中,此处为found_jsons

import json
open_chars = 0
saved_content = []
found_jsons = []
for i in content.splitlines():
open_chars += i.count('{')
if open_chars:
saved_content.append(i)
open_chars -= i.count('}')

if open_chars == 0 and saved_content:
found_jsons.append(json.loads('n'.join(saved_content)))
saved_content = []

for i in found_jsons:
print(json.dumps(i, indent=4))

输出

{
"results": [
{
"numberID": "7412",
"filename": "xxxx"
},
{
"numberID": "E52",
"filename": "xgjhh"
}
],
"name": "1111"
}
{
"results": [
{
"numberID": "478962",
"filename": "hhhhh"
},
{
"number": "12544",
"filename": "jkhgfc"
}
],
"name": "jfkjgjkf"
}

最新更新