我希望在 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()
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()
钻取并提取:
然后我通过 ... 解析它
Logical.FromText("true")
...产生 logical
结果。
true
Rgx_Tests()
的预期脚本
失败无法渲染 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()
:进行钻取和提取
然后我可以通过 ... 进行解析
Json.Document("[true,false,false,true]")
...产生 list
的
logical
结果。
{true, false, false, true}
失败record
) 在
Web.Page()
输出 (table
) 中完全不存在
:
预期
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()
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
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>",