正则表达式来减少巨大的文件大小?

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 块,我就可以逐个提取我需要的所有东西,也许是这样的逻辑:

    删除所有内容 除了:timestampMsWALKING 出现在*同一组[方括号]之间时:

    我需要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

这不是非常好,但肯定不错。

【讨论】:

以上是关于正则表达式来减少巨大的文件大小?的主要内容,如果未能解决你的问题,请参考以下文章

Python 基础入门 8_1 正则表达式

火箭炮:常用正则表达式 收藏!

Nginx中的正则如何匹配数字

正则表达式实例

正则表达式验证密码必须由大小写字母、数字、特殊字符组成

正则表达式