Weboldal keresés

powerbi - Regexp a Power Query-ben JavaScript használatával


Szükségem van reguláris kifejezésekre, míg az M/Power Query natív támogatással nem rendelkezik hozzá. Találtam többféle megoldást ugyanarra a kiváló Web.Page & JavaScript ötletre - Biccauntant, Hugoberry.

El kellett fogadnom őket (az eredményül kapott kód alább található) a JavaScript korlátai miatt.

A fő probléma az, hogy a JavaScript-nek megvan a saját kemény korlátja a String változókon - nem lehetséges használni benne a "\" karaktert.

Az én változatom kezeli a problémát, de nem vagyok profi JS-ben, szóval a fő kérdés az - gyanítom, hogy a "\" probléma nem az egyetlen.

Tehát a kérdésem az, hogy - lát valaki más problémákat a PQ és a JavaScript „együttműködésében”, miközben a reguláris kifejezést használja? Más buktatókkal kapcsolatban a karakterláncokkal vagy bármi mással kapcsolatban?

Értem, hogy a Regexp-nek megvannak a saját escape szabályai (ez nemcsak a JavaScript követelménye, hanem magáé a Regexp-é is), így a Regex saját magára vonatkozó escape-elése kívül esik a kérdés hatáskörén. Vagyis a reguláris kifejezést megfelelően le kell escape-elni, MIELŐTT mint függvényparaméter átadható lenne. Más szóval, feltételezzük, hogy ha a felhasználó reguláris kifejezést akar használni "\" karakterrel, akkor helyette "\\"-t kell használnia, és a függvényem ezt ""\"-re fogja konvertálni, amelyet a JavaScript Regexp kifejezésként fogad el.

PQ felhasználóknak - kiderült, hogy a megoldás teljesítménye legalább több száz sor esetén egészen jó. De ne felejtsük el, hogy a funkciót a Power BI szolgáltatásban nem lehet használni, ez csak a Power BI Desktophoz és az Excelhez alkalmas.

A kód:

(text as nullable text, pattern as nullable text) as logical =>
    let 
        l = List.Transform({text, pattern}, each Text.Replace(_, "\", "\\")), 
        t = Text.Format("<script>document.write(new RegExp('#{1}').test('#{0}'))</script>", l),
        w = Web.Page(t), 
        d = w[Data]?{0}?[Children]?{0}?[Children]?{1}?[Text]?{0}?, 
        result = text <> null and (pattern = null or (if d <> null then Logical.FromText(d) else error "Regular expression or text are not supported by JavaScript."))
    in
        result

Válasz #1

A fenti kód alapján (és az aposztrófok ' helyes elkerülésével) a következő függvényeket javaslom (3 függvényt tartalmazok, egyet a teszteléshez, egyet az egyezéshez és egyet a cseréhez).

MEGJEGYZÉS: Van egy probléma, amit nem tudok megoldani. A megfeleltetési függvényben a visszaadott lista hibás lesz, ha a karakterlánc vagy a találatok vesszőt (',') tartalmaznak. Megpróbáltam a JSON.stringify() használatát a találatok kiíratásához, de ez nem működik, itt felteszek egy kérdést erről, és később megpróbálom javítani, ha lehetséges.

Függvény reguláris kifejezés teszteléséhez

// regexTest
let   regexTest=(text as nullable text,replace as nullable text, optional flags as nullable text) as logical =>
    let
        f= if flags = null or flags ="" then "" else flags,
        l1 = List.Transform({text, replace}, each Text.Replace(_, "\", "\\")),
        l2 = List.Transform(l1, each Text.Replace(_, "'", "\'")),
        l3 = List.Combine({l2,{f}}),
        t = Text.Format("<script>document.write(new RegExp('#{1}','#{2}').test('#{0}'))</script>", l3),
        r = Web.Page(t)[Data]{0}[Children]{0}[Children]{1}[Text]{0},
        Output = Logical.From(r)
    in Output
    
in regexTest

Függvény reguláris kifejezés illesztéshez

// regexMatch
let   regexMatch=(text as nullable text, pattern as nullable text, optional flags as nullable text) as list =>
    let
        f=if flags = null or flags ="" then "" else flags,
        l1 = List.Transform({text, pattern}, each Text.Replace(_, "\", "\\")),
        l2 = List.Transform(l1, each Text.Replace(_, "'", "\'")),
        t = Text.Format("<script>var txt='#{0}';document.write(txt.match(new RegExp('#{1}','#{2}')));</script>", List.Combine({l2,{f}})),
        r=Web.Page(t)[Data]{0}[Children]{0}[Children],
        Output=if List.Count(r)>1 then Text.SplitAny(r{1}[Text]{0},",") else {}
    in Output
in regexMatch

Függvény reguláris kifejezés cseréhez

// regexReplace
let   regexReplace=(text as nullable text,pattern as nullable text,replace as nullable text, optional flags as nullable text) as text =>
    let
        f=if flags = null or flags ="" then "" else flags,
        l1 = List.Transform({text, pattern, replace}, each Text.Replace(_, "\", "\\")),
        l2 = List.Transform(l1, each Text.Replace(_, "'", "\'")),
        t = Text.Format("<script>var txt='#{0}';document.write(txt.replace(new RegExp('#{1}','#{3}'),'#{2}'));</script>", List.Combine({l2,{f}})),
        r=Web.Page(t)[Data]{0}[Children]{0}[Children],
        Output=if List.Count(r)>1 then r{1}[Text]{0} else ""
    in Output
in regexReplace

Válasz #2

Tudok JavaScriptet, de nem ismerem a Powerbit/Powerquery-t, szóval ez a válasz lehet, hogy hiányos.

Vannak más karakterek is, amelyeket escape-elni kell a karakterlánc literálokban.

Az egyszeres idézőjeles karakterlánc-literelek esetén a \ és a ' karaktereket el kell írni, akár néhány szóköz karaktert is, például újsor karaktert. Az elírások listája megtalálható a https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String oldalon. Néhány ezek közül opcionális, de nem vagyok biztos abban, pontosan melyek. Nem hiszem, hogy valaha is szükség lenne a hexadecimális számjegyekkel rendelkezőkre. Azt mondtam, hogy a ' karaktert el kell írni, de a "-ot nem, azonban ha dupla idézőjeles karakterláncot használtál volna ("#{0}"), ez fordítva lenne.

Ettől eltekintve nem tudom megmondani, milyen menekítést kell elvégezni a Powerquery számára. Valójában meglep, hogy a string literálod "\\" a várt módon működött.

Összefoglalva, valahogy így kellene kinéznie (és nyugodtan javítsd a Powerquery szintaxis hibáimat):

// Define function escapeString which inserts \ before each \ or ' and then applies the escape sequences \n, \r, \v, \t, \b and \f.
let 
    l = List.Transform({text, pattern}, each escapeString(_)), 
...