正则表达式来减少巨大的文件大小?
Posted
技术标签:
【中文标题】正则表达式来减少巨大的文件大小?【英文标题】:Reg Expression to reduce huge file size? 【发布时间】:2018-09-15 05:42:34 【问题描述】:我有一系列巨大 (40-80mb) 导出的 Google 位置历史记录JSON
文件,我的任务是分析选定的活动数据。不幸的是,谷歌在他们的download site 没有参数或选项来选择除了“一个包含永远的巨型 JSON”之外的任何东西。 (KML
选项是两倍大。)
JSON-Converter
(laexcel-test incarnation of VBA-JSON
) 等明显的选择;用VBA逐行解析;甚至Notepad++。它们都崩溃和燃烧。我在想 RegEx 可能是答案。
This Python script 可以在两秒内从 40mb 文件中提取时间戳和位置(使用 RegEx?)。 Python 是如何做到这么快的?(在 VBA 中会这么快吗?)
只要我有一个神奇的RegEx
块,我就可以逐个提取我需要的所有东西,也许是这样的逻辑:
删除所有内容 除了:
当timestampMs
和WALKING
出现在*同一组[
方括号]
之间时:
timestampMS
、和、后面的 13 位数字
WALKING
后面的一到三位数字。
如果包含更多数据更简单,例如“所有时间戳”或“所有活动”,我可以稍后轻松筛选。 目标是使文件足够小,这样我就可以在不需要rent a supercomputer 的情况下操作它,哈哈。
我尝试调整现有的 RegEx,但我对 RegEx 和乐器都有一个严重的问题:我没有努力,我只是 无法理解它。所以,这确实是一个“请为我写代码”的问题,但它只是一种表达方式,我今天将通过为别人写代码来回报它!谢谢... ☺ .
,
"timestampMs" : "1515564666086", ◁― (Don't need this but it won't hurt)
"latitudeE7" : -6857630899,
"longitudeE7" : -1779694452999,
"activity" : [
"timestampMs" : "1515564665992", ◁― EXAMPLE: I want only this, and...
"activity" : [
"type" : "STILL",
"confidence" : 65
, ↓
"type" : "TILTING",
"confidence" : 4
,
"type" : "IN_RAIL_VEHICLE",
"confidence" : 20 ↓
,
"type" : "IN_ROAD_VEHICLE",
"confidence" : 5
,
"type" : "ON_FOOT", ↓
"confidence" : 3
,
"type" : "UNKNOWN",
"confidence" : 3
,
"type" : "WALKING", ◁―┬━━ ...AND, I also want this.
"confidence" : 3 ◁―┘
]
]
,
"timestampMs" : "1515564662594", ◁― (Don't need this but it won't hurt)
"latitudeE7" : -6857630899,
"longitudeE7" : -1779694452999,
"altitude" : 42
,
编辑:
出于测试目的,我制作了一个示例文件,代表原始文件(大小除外)。原始 JSON 可以直接从 this Pastebin link 加载,或下载为本地副本,this TinyUpload link,或复制为下面的“一长行”:
"locations" : [ "timestampMs" : "1515565441334","latitudeE7" : 123456789,"longitudeE7" : -123456789,"accuracy" : 2299, "timestampMs" : "1515565288606","latitudeE7" : 123456789,"longitudeE7" : -123456789,"accuracy" : 12,"velocity" : 0,"heading" : 350,"altitude" : 42,"activity" : [ "timestampMs" : "1515565288515","activity" : [ "type" : "STILL","confidence" : 98, "type" : "ON_FOOT","confidence" : 1, "type" : "UNKNOWN","confidence" : 1, "type" : "WALKING","confidence" : 1 ] ], "timestampMs" : "1515565285131","latitudeE7" : 123456789,"longitudeE7" : -123456789,"accuracy" : 12,"velocity" : 0,"heading" : 350,"altitude" : 42, "timestampMs" : "1513511490011","latitudeE7" : 123456789,"longitudeE7" : -123456789,"accuracy" : 25,"altitude" : -9,"verticalAccuracy" : 2, "timestampMs" : "1513511369962","latitudeE7" : 123456789,"longitudeE7" : -123456789,"accuracy" : 25,"altitude" : -9,"verticalAccuracy" : 2, "timestampMs" : "1513511179720","latitudeE7" : 123456789,"longitudeE7" : -123456789,"accuracy" : 16,"altitude" : -12,"verticalAccuracy" : 2, "timestampMs" : "1513511059677","latitudeE7" : 123456789,"longitudeE7" : -123456789,"accuracy" : 16,"altitude" : -12,"verticalAccuracy" : 2, "timestampMs" : "1513510928842","latitudeE7" : 123456789,"longitudeE7" : -123456789,"accuracy" : 16,"altitude" : -12,"verticalAccuracy" : 2,"activity" : [ "timestampMs" : "1513510942911","activity" : [ "type" : "STILL","confidence" : 100 ] ], "timestampMs" : "1513510913776","latitudeE7" : 123456789,"longitudeE7" : -123456789,"accuracy" : 15,"altitude" : -11,"verticalAccuracy" : 2,"activity" : [ "timestampMs" : "1513507320258","activity" : [ "type" : "TILTING","confidence" : 100 ] ], "timestampMs" : "1513510898735","latitudeE7" : 123456789,"longitudeE7" : -123456789,"accuracy" : 16,"altitude" : -12,"verticalAccuracy" : 2, "timestampMs" : "1513510874140","latitudeE7" : 123456789,"longitudeE7" : -123456789,"accuracy" : 19,"altitude" : -12,"verticalAccuracy" : 2,"activity" : [ "timestampMs" : "1513510874245","activity" : [ "type" : "STILL","confidence" : 100 ] ] ]
使用JSONLint 和FreeFormatter 测试为有效的文件。
【问题讨论】:
奇怪的是,您发现在 Notepad++ 中处理 80MB 文件很困难。我在那里处理 500MB 文件时遇到问题,但 80MB 是可以的。恕我直言,您必须专注于解决您的主要问题,而不是 JSON 文件“缩小”。 JSON 解析工具应该可以毫无问题地处理巨大的 JSON 文件。 @WiktorStribiżew - 我们说的是 80 兆,就像 almsot 0.1gb 一样,对吧?还有“全部替换”功能?最后一个例子是用,\n\r
替换所有, \n
,在2 台不同的笔记本电脑(Win7、Win10,均为64 位)上,N++ 冻结。我等了 10 分钟然后放弃了。)大文件打开好吧,我就是不能运行这样的大型操作。这就是你所说的“主要问题”吗?这组特定的文件是一次性任务,但我可以想出很多方法来使这样的 RegEx 适应我正在处理的其他事情。
好吧,如果你只有 1 个文件用于一次性任务,我会坚持使用 NPP,但要有效地使用它。如果您需要将, \n
替换为,\n\r
,则不需要正则表达式,有扩展 模式。如果您有更多文件或将来会有类似的任务,我只会编写一个带有可自定义选项的 Python 脚本。我不会使用 VBA。顺便说一句,使用 Python,解析 JSON 真的很容易,只需 import json
。
@WiktorStribiżew 我无法编写任何可用的正则表达式,我在RegExOne、RegEx101、Rexegg 等上花费了无数时间;这是一个障碍。 最终目标:从 40-80mb 文件中获取“在示例中的第 2 和第 3 个箭头之间”的所有数据,转储到 Access 或 SQL Server 中进行分析。我只需要一个文件来正确处理,即使我必须一次处理一个activity
,然后我可以自动重复。 (正如我所提到的,可以实现这一点的“神奇 RegEx”也将被回收用于其他目的。)
@SimonShine - 还要澄清一下,Python 文件会使用不同的标准快速解析文件,我无法对其进行调整。我将该文件作为努力的证明和类似问题的解决方案。
【参考方案1】:
显而易见的选择...
这里显而易见的选择是可以快速处理大文件的 JSON 感知工具。下面我将使用jq,它可以轻松快速处理千兆字节大小的文件,只要有足够的RAM来保存文件在内存中,即使没有足够的RAM也可以处理非常大的文件。将 JSON 保存在内存中。
首先,让我们假设文件由所示形式的 JSON 对象数组组成,目标是为每个可接受的子对象提取两个值。
这是一个可以完成这项工作的 jq 程序:
.[].activity[]
| .timestampMs as $ts
| .activity[]
| select(.type == "WALKING")
| [$ts, .confidence]
对于给定的输入,这将产生:
["1515564665992",3]
更具体地说,假设上述程序位于名为 program.jq 的文件中,并且输入文件是 input.json,则合适的 jq 调用如下:
jq -cf program.jq input.json
应该很容易修改上面给出的 jq 程序来处理其他情况,例如如果 JSON 模式比上面假设的更复杂。例如,如果架构中有一些不规则,尝试在一些后缀?
s 中洒,例如:
.[].activity[]?
| .timestampMs as $ts
| .activity[]?
| select(.type? == "WALKING")
| [$ts, .confidence]
【讨论】:
看起来是一个很有前途的应用程序 - 但不太好用。每次尝试 JQ1.5/win64 crashed(我是 Win7/64 位)。 JQ1.5/Win32 运行但每个文件都给出了如下错误: jq:error (at LocationHistory20180109.json:1734218): Cannot index array with string "activity"
其中1734218
是文件的倒数第二行。 jq: error (at LocationHistory20180109.json:1734218): Cannot index array with string "activity"
最后两行: ]
。我假设一个快速修复? (无法解析最后一次出现等?)
另外我假设您刚刚将可执行文件从 jq-win32 或 jq-win64 重命名为 jq.exe ?
如果输入是无效的 JSON,这可能确实是一个挑战,但如果它只是不规则的,请尝试按照更新中的建议添加一些 ?s。如果这不起作用,也许您可以显示文件的最后一部分,例如***数组中的最后一项。 (要检查输入是否足够有效的 JSON,请尝试运行:jq empty input.json
)
我创建了一个用于链接或下载的示例文件(我在问题的底部添加了文件链接。)此版本的文件仍然在jq
中给出错误,但可以在线正确验证。感谢您的持续努力;我还没有时间尝试其他答案 - 它们看起来也很有希望 - 尽管我确实喜欢 jq
的声音,并且作为命令行实用程序,我也可以以编程方式调用它. (我很惊讶我以前从未听说过它!)
pastebin 文件在.locations
中有感兴趣的项目,因此您显然必须相应地修改 jq 程序(例如,将初始的 .
更改为 .locations
)。这样做之后,一切都很顺利。【参考方案2】:
你可以试试这个
(?s)^.*?\"longitude[^\[]*?\"activity[^\[]*\[[^\]]*?timestampMs\"[^\"\]]*\"(\d+)\"[^\]]*WALKING[^\]]*?confidence\"\s*:\s*(\b\d1,3\b)[^\]]*?\].*$
Regex Demo,,,我通过“longitude
”、“activity
”、“[
”、“@987654327”等关键字搜索并接近目标捕获值(timestamp value, walking value
) @"、"]
"、"walking
"、"confidence
"。
Python 脚本
ss=""" copy & paste the file contents' strings (above sample text) in this area """
regx= re.compile(r"(?s)^.*?\"longitude[^\[]*?\"activity[^\[]*\[[^\]]*?timestampMs\"[^\"\]]*\"(\d+)\"[^\]]*WALKING[^\]]*?confidence\"\s*:\s*(\b\d1,3\b)[^\]]*?\].*$")
matching= regx.match(ss) # method 1 : using match() function's capturing group
timestamp= matching.group(1)
walkingval= matching.group(2)
print("\ntimestamp is %s\nwalking value is %s" %(timestamp,walkingval))
print("\n"+regx.sub(r'\1 \2',ss)) # another method by using sub() function
输出是
timestamp is 1515564665992
walking value is 3
1515564665992 3
【讨论】:
【参考方案3】:不幸的是,您似乎选择了一种没有高性能 JSON 解析器的语言。
使用 Python,您可以:
#!/usr/bin/env python3
import time
import json
def get_history(filename):
with open(filename) as history_file:
return json.load(history_file)
def walking_confidence(history):
for location in history["locations"]:
if "activity" not in location:
continue
for outer_activity in location["activity"]:
confidence = extract_walking_confidence(outer_activity)
if confidence:
timestampMs = int(outer_activity["timestampMs"])
yield (timestampMs, confidence)
def extract_walking_confidence(outer_activity):
for inner_activity in outer_activity["activity"]:
if inner_activity["type"] == "WALKING":
return inner_activity["confidence"]
if __name__ == "__main__":
start = time.clock()
history = get_history("history.json")
middle = time.clock()
wcs = list(walking_confidence(history))
end = time.clock()
print("load json: " + str(middle - start) + "s")
print("loop json: " + str(end - middle) + "s")
在我的 98MB JSON 历史记录中打印:
加载 json: 3.10292s 循环json:0.338841s
这不是非常好,但肯定不错。
【讨论】:
以上是关于正则表达式来减少巨大的文件大小?的主要内容,如果未能解决你的问题,请参考以下文章