Power Query for Excel 中本机正则表达式的 JS Hack

问题描述 投票:0回答:1

背景

我希望在 Power Query (M) for Excel 中执行正则表达式检测(和匹配?),它缺乏对正则表达式的本机支持。与 Power BI 不同,Excel 缺乏与 R 和 Python 的集成,因此我无法使用它们来外包正则表达式操作。

方法

受到 u/tirlibibi17 的这个聪明的黑客的启发,我通过将 M 与 JavaScript (JS) 及其正则表达式功能集成来概括了本机解决方案。请参阅下面的解决方案部分了解我的完整工作。

虽然我已经在单个

Rgx_Test()
字符串上成功测试了
text
,但它在规模上陷入了困境,因为它为正在测试的 每个
 字符串重新生成了 
Web.Page()。使用复数
Rgx_Tests()
,我渴望对一批许多这样的字符串进行一次操作。

我的

Rgx_Tests()
的“预期脚本”应该改进
Rgx_Test()
的“当前脚本”。 M 中的原生
Json.*()
系列有助于与 JS 脚本交换批量数据,如 JSON 表示法所示。对于输入,M 中
list
text
变成 JS 中
Array
String
。对于输出,JS 中
Array
Boolean
变为 M 中
list
logical

问题

不幸的是,一次神秘的失败缩短了我在

Rgx_Tests()
上的进展:

  • 虽然我当前的脚本通过

    Boolean
     正确渲染了 
    document.write()
    ,但我预期的脚本无法同样渲染 JSON 文本
    String
    。这个
    String
     是由 
    JSON.stringify()
    Array
    s 的 
    Boolean
     生成的。

  • 这个问题出现在M中,而出现在JS中,如单独的预期脚本所示(如下)。大概问题在于Web.Page()

    ,但为什么它应该渲染
    Boolean
    却无法渲染
    String

  • 没有抛出任何错误。当前脚本将

    Boolean

     呈现在文本块中,我可以在其中轻松找到它。对于预期的脚本,
    这个文本块只是丢失了

有关详细信息,请参阅下面的

调试部分。

问题

这个神秘故障的根源是什么,我该如何解决它?

do知道JS脚本在语法或运行时有错误,会产生这种行为。然而,该 JS 脚本(如下)可以完美地自行运行。 也许像JSON.stringify()

这样的函数与M使用的JS版本不兼容?


调试

当前脚本

Rgx_Test()

我当前的脚本工作顺利,并呈现原始文本供 M 解释为

logical

我采用 M 中的单个

text

 字符串,并通过我的助手将其转换 
Json_Stringify()
...

Json_Stringify("A1")
...进入其 JSON 表示形式...

"A1"
...我将其“注入”(拼接)到下面的 JS 脚本中。

<html><body><script> // ˇˇˇˇ var txt = "A1"; // ^^^^ var ptn = "[a-z]\\d"; var flg = "i"; var rgx = new RegExp(ptn, flg); var tst = rgx.test(txt); document.write(tst); </script></body></html>

它将结果呈现为原始文本...

true
...我通过 

Web.Page()

 钻取并提取:

7

然后我通过

Logical.FromText()

... 解析它

Logical.FromText("true")
...产生 

logical

 结果。

true

Rgx_Tests()
的预期脚本

我渴望下面的脚本,它在 JS 中成功,但

失败无法渲染 JSON 文本让 M 解释为 list

s 的 
logical

我在 M 中取

list

text
,并通过我的助手将其转换 
Json_Stringify()
...

Json_Stringify({"A1", "2b", "c_3", "d4_"})
...进入其 JSON 表示形式...

["A1","2b","c_3","d4_"]
...我将其“注入”(拼接)到下面的 JS 脚本中:

<html><body><script> // ˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇ var txts = ["A1","2b","c_3","d4_"]; // ^^^^^^^^^^^^^^^^^^^^^^^ var ptn = "[a-z]\\d"; var flg = "i"; var rgx = new RegExp(ptn, flg); function rgx_test(txt) {return rgx.test(txt)}; var tsts = txts.map(rgx_test); var out = JSON.stringify(tsts); document.write(out); </script></body></html>

应该将结果呈现为(JSON)文本...

[true,false,false,true]
...我可以通过 

Web.Page()

:
进行钻取和提取

9

然后我可以通过

Json.Document()

... 进行解析

Json.Document("[true,false,false,true]")
...产生 

list

logical
 结果。

{true, false, false, true}
失败

不幸的是,该文本块的相应行 (

record

) 在 
Web.Page() 输出 (table
) 中
完全不存在

11

预期

Rgx_Tests()

这应该针对正则表达式

list

 测试 
text
pattern
 字符串,并返回 
list
 结果的 
logical

let Rgx_Tests = ( texts as list, pattern as text, optional insensitive as nullable logical ) as nullable list => let txts = Json_Stringify(texts), ptn = Json_Stringify(pattern), flg = Json_Stringify(Rgx_Flags(false, false, insensitive)), html = "<html><body><script> var txts = " & txts & "; var ptn = " & ptn & "; var flg = " & flg & "; var rgx = new RegExp(ptn, flg); function rgx_test(txt) {return rgx.test(txt)}; var tsts = txts.map(rgx_test); var out = JSON.stringify(tsts); document.write(out); </script></body></html>", page = Web.Page(html), out = page{0}[ Data ]{[ Name = "HTML" ]}[Children]{[ Name = "BODY" ]}[Children]{[ Kind = "Text" ]}[ Text ], result = try Json.Document(out) otherwise null in result in Rgx_Tests


解决方案

这是我目前

可以工作的功能。

Rgx_Flags()

这个助手为 JS 生成

regex flags 字符串。

let Rgx_Flags = ( optional hasIndices as nullable logical, optional global as nullable logical, optional ignoreCase as nullable logical, optional multiline as nullable logical, optional dotAll as nullable logical, optional unicode as nullable logical, optional unicodeSets as nullable logical, optional sticky as nullable logical ) as text => let MOD_SEP = "", MOD_NOT = "", MOD_FLAGS = [ hasIndices = "d", global = "g", ignoreCase = "i", multiline = "m", dotAll = "s", unicode = "u", unicodeSets = "v", sticky = "y" ], ind = if hasIndices = true then MOD_FLAGS[hasIndices] else MOD_NOT, glo = if global = true then MOD_FLAGS[global] else MOD_NOT, ign = if ignoreCase = true then MOD_FLAGS[ignoreCase] else MOD_NOT, mul = if multiline = true then MOD_FLAGS[multiline] else MOD_NOT, dot = if dotAll = true then MOD_FLAGS[dotAll] else MOD_NOT, uni = if unicode = true then MOD_FLAGS[unicode] else MOD_NOT, uns = if unicodeSets = true then MOD_FLAGS[unicodeSets] else MOD_NOT, sti = if sticky = true then MOD_FLAGS[sticky] else MOD_NOT, result = Text.Combine({ind, glo, ign, mul, dot, uni, uns, sti}, MOD_SEP) in result in Rgx_Flags

Json_Stringify()

此助手将

value

 转换为其 JSON 表示形式。

let Json_Stringify = ( value as any, optional encoding as nullable number ) as nullable text => Text.FromBinary(Json.FromValue(value, encoding), encoding) in Json_Stringify

Rgx_Test()

这会针对正则表达式

text

 测试单个 
pattern
 字符串,并返回 
logical
 结果。

let Rgx_Test = ( text as text, pattern as text, optional insensitive as nullable logical ) as nullable logical => let txt = Json_Stringify(text), ptn = Json_Stringify(pattern), flg = Json_Stringify(Rgx_Flags(false, false, insensitive)), html = "<html><body><script> var txt = " & txt & "; var ptn = " & ptn & "; var flg = " & flg & "; var rgx = new RegExp(ptn, flg); var tst = rgx.test(txt); document.write(tst); </script></body></html>", page = Web.Page(html), tst = page{0}[ Data ]{[ Name = "HTML" ]}[Children]{[ Name = "BODY" ]}[Children]{[ Kind = "Text" ]}[ Text ], result = try Logical.From(tst) otherwise null in result in Rgx_Test
    
javascript html regex powerquery m
1个回答
0
投票
我认为 PQ 不允许任何 JS 在其 Web 容器中循环。结果, array.map 不起作用,即使它在 JS fiddle 中工作并且避免了数组原型方法,它也不起作用:

html = "<html><body><script> var txts = " & txts & "; var ptn = " & ptn & "; var flg = " & flg & "; var rgx = new RegExp(ptn, flg); var out = []; for (let i = 0; i < txts.length; i++) { out[i] = rgx.test(txts[i]); } var out2 = JSON.stringify(out); document.write(out2); </script></body></html>",
    
© www.soinside.com 2019 - 2024. All rights reserved.