Lua Beispielpatterns

Zur Navigation springen Zur Suche springen

Anmerkungen

Lua Patterns basieren nur lose auf regulären Ausdrücken. So kann man beispielsweise nur zwischen einzelnen Zeichen wählen, nicht zwischen ganzen Zeichenfolgen.

Der Grund dafür ist, dass die Auswertung reguläre Ausdrücke den Aufwand für das Ausführen von Lua-Code gewaltig erhöhen würde. Lua Patterns stellen nicht einmal eine Untermenge dar, sondern es gibt Abweichungen.

Einen String nach einem bestimmten Muster in Bestandteile zerlegen

Eine Tabelle von Farbnamen und Farbcodes liegt in Form einer Datei vor, wobei jede Zeile einem bestimmten Muster entspricht.

tomato tomato #FF6347 (255,99,71)

Eigentlich nicht schwer, allerdings kann der erste Teil aus mehreren Wörtern bestehen.

pale golden rod palegoldenrod #EEE8AA (238,232,170)

Ich habe ein Muster entworfen, das den String in die Farbbezeichnung mit Leerzeichen, die Farbbezeichnung in einem Wort, den Hex-Code und den RGB-Code als einzelne Zahlen zerlegt. Eine Zerlegung des Strings vorab in Zeilen ist bedingt durch das Suchmuster nicht notwendig. Außerdem habe ich bewusst auf Wiki-eigene Textfunktionen in Lua verzichtet, damit kann dieses Beispiel auch in einer anderen Lua-Installation verwendet werden.

"%s?([%w%s]+)[%s]([%w]+)[%s]+(#%x%x%x%x%x%x)[%s]+[(](%d+)%p+(%d+)%p+(%d+)[)]"

Man könnte den Teil zwischen den Klammern auch als balanced pattern erhalten, allerdings müssen die Zahlen nochmals aufbereitet werden

local farben = [[
coral coral #FF7F50 (255,127,80)
indian red indianred #CD5C5C (205,92,92)
light coral lightcoral #F08080 (240,128,128)
]]

local mypat = "%s?([%w%s]+)[%s]([%w]+)[%s]+(#%x%x%x%x%x%x)[%s]+(%b())"
local numpat = "(%d+)%p+(%d+)%p+(%d+)"

for aa, alpha, hex, rgb in farben:gmatch(mypat) do
  r,g,b=rgb:match(numpat)
  print(aa:gsub(" ","-"), alpha, hex, rgb, r, g, b)
end

Resultat

coral	coral	#FF7F50	(255,127,80)	255	127	80
indian-red	indianred	#CD5C5C	(205,92,92)	205	92	92
light-coral	lightcoral	#F08080	(240,128,128)	240	128	128

Erklärung des Musters

local mypat = "%s?([%w%s]+)[%s]([%w]+)[%s]+(#%x%x%x%x%x%x)[%s]+[(](%d+)%p+(%d+)%p+(%d+)[)]"

  • ich fange von hinten an
  • [(](%d+)%p+(%d+)%p+(%d+)[)] eine öffnende Klammer gefolgt von Ziffern, dann optionale Satzzeichen, gefolgt von Ziffern usw, bis zu einer schließenden Klammer
  • alternativ: (%b()) liefert den String zwischen Klammern inklusive Klammern und muss noch weiter zerlegt werden mit "(%d+)%p+(%d+)%p+(%d+)"
  • (#%x%x%x%x%x%x)[%s]+ genau sechs Hexadezimalzahlen gefolgt von mindestens einem Weißraumzeichen
  • %s?([%w%s]+)[%s]([%w]+)[%s]+ ein optionales Weißraumzeichen %s? gefolgt von einer Zeichenkette, die aus Weißraumzeichen und alphanumerischen Zeichen besteht ([%w%s]+) danach ein Weißraumzeichen [%s] gefolgt von einer Zeichenkette aus alphanumerischen Zeichen, die von einem Weißraumzeichen gefolgt wird ([%w]+)[%s]+
  • in Kombination ergibt sich das gewünschte Suchmuster

Lua-Patterns

string.match("test_12345_nochmehrtest.yyy", ('%d%d%d%d%d?%d?%d?%d?'))

ergibt 12345 Dieses Muster liefert das erste Auftreten von vier aufeinanderfolgenden Ziffern (0-9) und optional bis zu vier weitere Ziffern. Kommen höchstens 3 aufeinanderfolgende Ziffern im Suchtext vor, wird nil zurück gegeben.

Datumswerte aus einem Textstring extrahieren

local _ = "xxx1.4.2020ccc":match('%d+[%.%-]%d+[%.%-]%d+')

ergibt 1.4.2020 Dabei wird nicht überprüft, ob es sich um ein gültiges Datum handelt. Es wird lediglich auf die Aufeinanderfolge von 3 Zahlen, getrennt durch Punkte oder Bindestriche, getestet.

local _ = string.match("xxx1.41.2020ccc",'%d+[%.%-]%d+[%.%-]%d+')

ergibt 1.41.2020

Datumswerte II

Das folgende Suchmuster kann ein Datum genauer eingrenzen. Es wird auf 1-2 Ziffern, gefolgt von einem Satzzeichen %p, 1-2 Ziffern, Satzzeichen %p sowie 2-4 Ziffern getestet.

string.match("xxx11.14.2020ccc",'[0123]?%d%p[01]?%d%p%d%d%d?%d?')
--> 11.14.2020

Wie man sieht, wird der Fehler Monat>12 nicht abgefangen. Das Jahr kann 2- oder 4stellig sein, ein dreistelliges Jahr wäre mit dieser Abfrage allerdings auch zulässig. Zulässig wäre auch ein Tag von 32-39 sowie 31. von kurzen Monaten. Die wesentlichen Eigenschaften eines Datums werden immerhin getestet.

Erweiterung aus dem Namen einer Mediendatei extrahieren

Der Name von Mediendateien enthält am Ende eine üblicherweise 3-4stellige Folge aus Buchstaben und Ziffern, sowie einen Punkt davor. Das folgende Muster liefert die Erweiterung eines Mediennamens, wobei auch Ziffern berücksichtigt[1] werden.. Zusätzlich wird die Zeichenfolge in Kleinbuchstaben umgewandelt.

string.lower(mw.title.getCurrentTitle().text):match('%w+$')

Matchtext nur mit Buchstaben

string.lower(mw.title.getCurrentTitle().text):match('%a+$')

Allgemein gesagt liefert dieses Patterns die längste Zeichenfolge am Ende des Seitennamens, die entweder nur aus Buchstaben oder aus alphanumerischen Zeichen besteht. Angewendet auf dieser Seite ergibt sich Beispielpatterns

Erweiterung aus dem Namen einer Mediendatei entfernen

Sobald ich eine einfachere Funktion gefunden habe, werde ich sie hier eintragen.

Zunächst wird die Erweiterung der Datei festgestellt, anschließend dieser Text entfernt. Das kann natürlich zur Folge haben, dass unerwünschte Ersetzungen stattfinden.

string.gsub(mw.title.getCurrentTitle().text, mw.title.getCurrentTitle().text:match('%.%a+$'),'')

Weißraum aus beiden Enden eines Strings entfernen

Zur Vermeidung von Scriptfehlern auf Eigenschaft string testen!
if type(s) ~= 'string' then return s end
s:match("^%s*(.-)%s*$")

Weißraum + Steuerzeichen

s:match("^[%s%c]*(.-)[%s%c]*$")

Einen Text vom Ende eines Strings entfernen

Falls man eine Zahl folgendermaßen formatiert hat

s = string.format("%.2f", number)

hat der Textstring auf jedenfall zwei Nachkommastellen, auch wenn diese Null sind. Mit folgendem Befehl kann man die überflüssigen Nachkommastellen entfernen:

s:gsub('%.00$','')

Führende Nullen kann man folgendermaßen entfernen:

s:gsub('^0*','')
a=string.format("%.2f", 3.000777)
b=a:gsub('%.00$','')
print(a,b)
--3.00	3
a='000055.4'
b=a:gsub('^0*','')
print(a,b)
--000055.4	55.4
s:match('(.-)%.00$')

Liefert nur dann ein Ergebnis, wenn der gesuchte String tatsächlich vorhanden ist. Man könnte das Ergebnis noch ergänzen, dann passt es wieder.

s:match('(.-)%.00$') or s
a=string.format("%.2f", 30.00777)
b=string.format("%.2f", 30.000777)
c=a:match('(.-)%.00$') or a
d=b:match('(.-)%.00$') or b
print(a,c,b,d)
--30.01	30.01	30.00	30

Text bis zum ersten &

local a = 'Module:T&action=edit&oldid=968&action=edit'
print(a:match("([^&]*)"))
--Module:T

Funktioniert auch, wenn kein & im Vergleichstext vorkommt, in diesem Fall wird der komplette String zurück geliefert

Parameter inklusive Wert

Der Parameter wird durch ein & getrennt, nach dem = steht ein Zahlenwert. or '' verhindert, dass ein nil-Wert zurückgeliefert wird.

local a = 'Module:T'
print(a:match("(&oldid=[%d]*)") or '')
-- Leerstring

local a = 'Module:T&action=edit&oldid=968'
print(a:match("(&oldid=[%d]*)") or '')
-- &oldid=968

Einen String entfernen

a = "<br>aaa|<br>bbb=c"
x,y = a:gsub("<br>","")
print(a:gsub("<br>",""))
print(x)

ergibt

aaa|bbb=c	2
aaa|bbb=c

Lua-Prozedur zum Testen von Suchmustern

Es ist nicht immer einfach, ein passendes Pattern zu entwerfen. Jedesmal einen Modul zu ändern, bläht die Versionsgeschichte des Moduls unnötig auf und erhöht die Zahl der unnötigen Seitenbearbeitungen.

Mit dem folgenden Modul kann man beliebige Suchpatterns testen. Den Aufruf {{#invoke:Hili|pattern|pattern=}} kann man als Vorlage expandieren[2] und das Ergebnis testen. Der Einfachheit halber extrahiert die Prozedur den Suchbegriff aus dem Seitennamen, den man als Kontexttitel eingeben kann. Es wäre relativ einfach möglich, einen zusätzlichen Parameter für den zu durchsuchenden Text einzuführen.

function p.pattern(frame)
	-- mit dieser Funktion kann man die Funktionsweise eines Lua-Patterns testen
	-- das Pattern wird als Parameter übergeben, als Teststring dient der Seitennamen
	
	local a = frame.args              -- direkt per #invoke: übergeben
	local b = frame:getParent().args  -- an die Vorlage übergebene Parameter
	
	local muster = a["pattern"] or b["pattern"] or ''
	local _ = ''
	if muster == '' then return 'Vergleichsmuster fehlt' end

	local myname = string.lower(mw.title.getCurrentTitle().text) 
	_ = myname:match(muster)
	if _ == '' or _ == nil then _ = 'Muster ' .. muster .. ' in Namen ' .. myname .. ' nicht gefunden' end
	return _
end

Noch besser kann man Lua-Kommandos direkt auf der offiziellen Lua-Seite testen: Lua Demo Online

Links