
本教程旨在解决使用python从json数据中提取键值对时,如何正确处理包含空格的属性名或值。通过分析`str.split()`方法的行为,我们将重点介绍如何利用其`maxsplit`参数来精确控制字符串分割,从而确保多词属性值能够完整保留,避免数据丢失,并提供优化后的简洁代码实现。
在处理从jsON数据中提取的文本行时,尤其当这些行包含非标准格式的键值对(例如,值和属性名之间有多个空格,且属性名本身也包含空格)时,常常会遇到数据解析的挑战。本教程将深入探讨如何使用python有效地解析这类数据,确保所有信息,特别是包含空格的属性名,能够被完整地提取和保留。
问题分析:多词属性名的解析困境
假设我们从json数据的 payload.blob.rawLines 路径中获取到一系列字符串,这些字符串代表着数值和对应的属性名。原始数据可能存在不规则的空白字符,并且某些属性名由多个单词组成,例如 “Property1_word1 Property1_word2″。
以下是原始数据中提取出的两行示例:
" C_1H_4 Methane " " 5.00000 Property1_word1 Property1_word2 "
在初步处理这些行时,通常会先移除首尾空白并规范化内部空白,得到如下形式的字符串列表:
立即学习“Python免费学习笔记(深入)”;
trimmed = ['C_1H_4 Methane', '5.00000 Property1_word1 Property1_word2']
如果使用 e.split(‘ ‘)(不带任何参数的 split() 方法)来将这些字符串分割成键值对,会遇到以下问题:
for e in trimmed: print(e.split(' '))
输出结果:
['C_1H_4', 'Methane'] ['5.00000', 'Property1_word1', 'Property1_word2']
可以看到,对于包含多词属性名(如 “Property1_word1 Property1_word2″)的字符串,e.split(‘ ‘) 会将其分割成多个部分。例如,’5.00000 Property1_word1 Property1_word2’ 被分割为 [‘5.00000’, ‘Property1_word1’, ‘Property1_word2’]。如果此时我们尝试通过 e.split(‘ ‘)[0] 获取值,并通过 e.split(‘ ‘)[1] 获取属性名,那么 Property1_word2 部分就会被错误地丢弃。
解决方案:利用 str.split() 的 maxsplit 参数
Python 的 str.split() 方法提供了一个 maxsplit 参数,它允许我们指定最大分割次数。通过设置 maxsplit=1,我们可以确保字符串只被分割成两部分:第一个分隔符之前的部分和第一个分隔符之后的所有剩余部分。
将 e.split(‘ ‘) 修改为 e.split(‘ ‘, 1):
Easily find JSON paths within JSON objects using our intuitive Json Path Finder
30 for e in trimmed: print(e.split(' ', 1))
输出结果:
['C_1H_4', 'Methane'] ['5.00000', 'Property1_word1 Property1_word2']
现在,每个字符串都被正确地分割成了两部分。第一部分是数值(或标识符),第二部分是完整的属性名,即使它包含空格。
因此,构建字典的代码可以修改为:
as_dict = {e.split(' ')[0]: e.split(' ', 1)[1] for e in trimmed}
这将确保字典中的键是数值,而值是完整的、包含空格的属性名。
优化代码实现
上述方法虽然解决了问题,但在处理 stripped 和 trimmed 列表时,存在一些冗余操作。我们可以进一步优化代码,使其更简洁高效。
str.split() 方法在不指定分隔符(即使用 split() 或 split(None))时,会根据任意空白字符进行分割,并自动处理多个连续空白,同时移除结果列表中的空字符串。结合 maxsplit=1,这成为一个强大的工具。
我们可以直接在原始的 rawLines 上进行迭代,并结合 strip() 和 split(None, 1) 来一步到位地完成数据提取和字典构建。
import json import pandas as pd # 模拟从文件加载JSON数据 # 实际应用中,您会从文件或网络请求中加载 json_data_str = """ { "payload": { "blob": { "rawLines": [ " C_1H_4 Methane ", " 5.00000 Property1_word1 Property1_word2 ", " 20.00000 Property2 ", " 500.66500 Property3 ", " 100.00000 Property4_word1 Property4_word2 ", " -4453.98887 Property5 ", " 100.48200 Property6 ", " 59.75258 Property7 ", " 5.33645 Property8_word1 Property8_word2 ", " 0.00000 Property9 ", " 645.07777 Property10 ", " 0.00000 Property11 ", " 0.00000 Property12 ", " 0.00000 Property13 ", " 0.00000 Property14 ", " 0.00000 Property15 ", " 0.00000 Property16 ", " 0.00000 Property17 ", " 0.00000 Property18 ", " 0.00000 Property19 ", " 0.00000 Property20 ", " 0.00000 Property21 ", " 0.00000 Property22 ", " 0.00000 Property23 ", " 0.00000 Property24 ", " 0.00000 Property25 ", " 0.57876 Property26 ", " 4.00000 Property27 ", " 0.00000 Property28 ", " 0.00000 Property29 ", " 0.00000 Property30 ", " 0.00000 Property31 ", " 0.00000 Property32 ", " 1.00000 Property33 ", " 0.00000 Property34 ", " 26.00000 Property35 ", " 1.44571 Property36 ", " 1.08756 Property37 ", " 0.00000 Property38 ", " 0.00000 Property39 ", " 0.00000 Property40 ", " 6.00000 Property41 ", " 9.00000 Property42 ", " 0.00000 Property43 " ] } } } """ data = json.loads(json_data_str) # 获取需要提取的原始行数据 to_extract = data["payload"]["blob"]["rawLines"] # 优化后的数据提取和字典构建 # 对于每一行: # 1. 使用 .strip() 移除行首尾所有空白字符。 # 2. 使用 .split(None, 1) 进行分割: # - None 作为分隔符表示按任意空白字符分割。 # - 1 表示最多分割一次,确保只将字符串分成两部分。 # 3. dict() 构造函数直接将这些两元素的列表转换为字典的键值对。 as_dict = dict(line.strip().split(None, 1) for line in to_extract) # 将字典转换为Pandas DataFrame # 字典的键将映射到 'Value' 列,值映射到 'Property' 列 df = pd.DataFrame(as_dict.items(), columns=['Value', 'Property']) print("优化后的字典内容:") print(as_dict) print("n生成的DataFrame:") print(df)
输出示例 (部分):
优化后的字典内容: {'C_1H_4': 'Methane', '5.00000': 'Property1_word1 Property1_word2', '20.00000': 'Property2', '500.66500': 'Property3', '100.00000': 'Property4_word1 Property4_word2', ...} 生成的DataFrame: Value Property 0 C_1H_4 Methane 1 5.00000 Property1_word1 Property1_word2 2 20.00000 Property2 3 500.66500 Property3 4 100.00000 Property4_word1 Property4_word2 ...
注意事项与总结
- str.split() 的灵活性:理解 split() 方法的 sep 和 maxsplit 参数至关重要。当 sep 为 None 时,它会智能地处理各种空白字符(空格、制表符、换行符等)并跳过空字符串,这对于清洗非结构化文本非常有用。
- 数据一致性:本教程的解决方案假定每行数据都至少包含一个值和一个属性名,并且值与属性名之间存在空白字符。如果原始数据格式更加复杂或不一致,可能需要更高级的正则表达式解析或定制的解析逻辑。
- 代码可读性与效率:优化后的单行代码 dict(line.strip().split(None, 1) for line in to_extract) 不仅简洁,而且避免了创建中间列表