如果值包含未转义的逗号,是否有一种简单的方法来解析 Excel、Power Query 或 VBA 中逗号分隔的 Key:Value 对?

Is there a simple way to parse comma separated Key:Value pairs in Excel, Power Query or VBA if the values contain unescaped commas?

我正在处理包含约 22000 条记录的身份数据的 CSV 导出。其中一个字段的标题为 'ExtendedAttributes',该列中的每个单元格都包含一个由逗号分隔的 Key:Value 对引号绑定字符串。文件中的每条记录都有任意数量的扩展属性(最多约 50 个)。我最终的 objective 是将这些扩展属性扩展到 Excel (2016) 中它们自己的列中。我已经有了使用公式、简单 VBA 和最近基于 Power Query 的方法从其他数据扩展到列的解决方案。

然而,我之前的解决方案都是基于易于分隔的 Key:Value 对。在此导出中,ExtendedAttributes 字段具有:

  1. 可能包含 unescaped/unquoted 逗号的数值数据。例如

    "Key1: Value1, name: surname, forename, Key2: Value2, ... "

  2. 可能包含多个逗号分隔值的键,它们也是 unquoted/unescaped。例如

    "Key1: Value1, emailAlias: alias1@domain, alias2@domain, alias3@domain, Key2: Value2, ... "

我通常的做法是,Key:Value 对没有这些问题,使用逗号分隔将其分成键值对,将数据转置为行,然后使用冒号分隔按照描述填充我的新列及其值 here in the PowerBI community pages

这在这里不起作用,因为使用逗号分隔会破坏值。

有没有一种直接的方法可以使用(理想情况下)Power Query 将其解析为构成 Key:Value 对?也很高兴使用 VBA 或基于公式的解决方案。

我的本能方法是尝试识别包含冒号的子字符串,并在它们前面加上一个唯一字符,然后可以将其用作分隔符。 (数据也可能包含未转义的冒号并非不可能,但我很乐意假设它不会)但认识到这可能是一种不必要的复杂方法,我不确定如何最好地做到这一点。

我很乐意将多个逗号分隔项的值作为一个单元保存(我稍后会处理这个问题)。

示例数据:

"Key1: Value1, name: surname, forename, emailAlias: alias1@domain, alias2@domain, alias3@domain, Key2: Value2, ... "

我想最终得到一些让我像这样处理数据的东西,也许使用!作为示例,我可以将其用作分隔符:

"Key1: Value1!name: surname, forename!emailAlias: alias1@domain, alias2@domain, alias3@domain!Key2: Value2!..."

我无权访问原始数据(供应商控制的系统)并且公司桌面上的数据处理工具有限(Excel 2016,VBA,PQ)。 感谢任何帮助。

你没有说什么文件记录不计入“ExtendedAttribute字段”类别...我准备了一个函数可以分离你讨论的那个区域。请使用下一个代码:

Function separateKeys(x As String, sep As String) As String
  Dim arr1, arr2, i As Long, k As Long
  
  arr1 = Split(x, ": ")
  ReDim arr2(UBound(arr1))
  For i = 0 To UBound(arr1) - 1
    If arr1(i + 1) = arr1(UBound(arr1)) Then Exit For
    arr2 = Split(arr1(i + 1), " ")
    arr2(UBound(arr2) - 1) = Replace(arr2(UBound(arr2) - 1), ",", sep)
    arr1(i + 1) = Join(arr2, " ")
  Next
  separateKeys = Replace(Join(arr1, ":"), sep & " ", sep)
End Function

上述函数可以(可能)以跳过文件其余部分计算的方式进行调整,或者也可以转换 sep 字符中的每个逗号(使用 Replace 即可)。

为了测试以上功能,请使用下一个测试Sub:

Sub testSepKeys()
  Dim x As String, sep As String
  
  sep = "|" 'you can try something else, but improbable to appear in the processed text
  x = "Key1: Value1, name: surname, forename, emailAlias: alias1@domain, alias2@domain, alias3@domain, Key2: Value2, Value3, key3: Val1, Val2"
  Debug.Print separateKeys(x, sep)
End Sub

像全局工作方式一样,我建议在行分隔符上拆分文件,然后使用上述(改编的)函数处理所有数组元素(行),最后在行分隔符上加入它。 新创建的文件应使用 Workbooks.OpenTextDataType:=xlDelimitedOtherChar:=sep.

打开

请测试以上功能并发送一些反馈。

在Power Query中,您可以定义一个函数Partition如下:

let
    Output = (str as text, sep as text) as text =>
    Text.RemoveRange(
        Text.Replace(
            Text.Combine(
                List.Transform(
                    Text.Split(str, " "),
                    each if Text.Contains(_, ":") then sep & _ else _
                ),
                " "
            ), ", " & sep, sep
        ),
    0, Text.Length(sep)
    )
in
    Output

使用分隔符的文本转换示例 !

起始文字:

Key1: Value1, name: surname, forename, emailAlias: alias1@domain
  1. 根据空格将字符串拆分为列表
Key1:
Value1,
name:
surname,
forename,
emailAlias:
alias1@domain
  1. 在任何包含 : 的列表项前加上分隔符 !
!Key1:
Value1,
!name:
surname,
forename,
!emailAlias:
alias1@domain
  1. 将列表组合回字符串
!Key1: Value1, !name: surname, forename, !emailAlias: alias1@domain
  1. , !替换为!
!Key1: Value1!name: surname, forename!emailAlias: alias1@domain
  1. 删除第一个分隔符
Key1: Value1!name: surname, forename!emailAlias: alias1@domain

定义此函数后,您可以在类似于

的列转换中调用它
= Table.TransformColumns(#"Prev Step", {{"ColName", each Partition(_,"!") , type text}})