Morrowind Mod:XStringMatch

The UESPWiki – Your source for The Elder Scrolls since 1995
Jump to: navigation, search

A function added by MWSE.

This function allows to perform a Regular Expression (also called R.E. or Regex) check on a string, allowing for very flexible and powerful pattern and string matching/identification. For example, you can check if a string contains certain words, or a certain set of characters, in it and in a specific position and order, if you want, and much more.

Note: Up until version v0.9.5-alpha.20150718 of MWSE by Merzasphor, an oversight in this function caused it to allow partial matches, causing undesired matches to successfully occur in certain cases.


Syntax and usage[edit]

match (long): xStringMatch source (string) pattern (string)

The above performs a case sensitive Regular Expression match on source using the pattern in pattern. Regular expressions are pattern matching expressions. MWSE uses the Regex library from the Boost C++ library, from http://www.boost.org. The regular expression style is the Perl syntax; please see the Boost documentation for full documentation. However, here is a partial quick summary:

.       Matches any one character.
^       Matches the beginning of the string.
$       Matches then end of the string.
( and ) Groups a sub-expression (see below).
*       Makes the preceeding item or group match zero or more times.
?       Makes the preceeding item or group match zero or once.
+       Makes the preceeding item or group match one or more times.
{integer}       Makes the preceeding item or group match exactly integer times.
{int1,int2}     Makes the preceeding item or group match from int1 to int2 times.
|       Alternation; makes the pattern match either the item on the left of the | or the item on the right.
[character set] Matches a single character from the set character set. The set can contain individual characters, or ranges of the form a-b, which matches all characters between a and b, including a and b. (For example, [A-Z] will match upper case letters.) If the character set begins with ^, then it will match any character except those in the character set.
[[:name:]]      Matches a single character from the pre-defined set name.
\character      Matches the character; that is, removes the special meaning, if any, from character. (For example, "." matches any single character, but "\." matches the period character.)
\d      Match a digit (0-9).
\l      Match a lower case letter (a-z).
\s      Match whitespace (space, tab).
\u      Match an upper case letter (A-Z).
\w      Match a word (a string of letters, digits, and underscores)
\D      Match anything but a digit.
\L      Match anything but a lower case letter (a-z).
\S      Match anything but whitespace (space, tab).
\U      Match anything but an upper case letter (A-Z).
\W      Match anything but a word.
\<   Match the start of a word.
\>   Match the end of a word.
\b      Match the end or start of a word.
\B      Match anything but the end or start of a word.
(?i)    Turns on the i modifier (case insensitive match) for the rest of the pattern or until an enclosing ).
(?i:pattern)    Turns on the i modifier for the enclosed pattern only.

This function will return the value 1 if a successful match was made, or the value 0 otherwise.

More regular expression syntax info and examples may be found at the Perl documentation, but note that that page can be more technical and some parts in it contain actual Perl language syntax as well, which a reader that does not know Perl may not grasp. You may want to try the Boost documentation (linked above), first.

Examples[edit]

Example 1: simplest example[edit]

The simplest use of this function is to test whether a string contains another string anywhere within it:

setx name to someNPC->xGetName
setx match to xStringMatch name "Guard"

match will be 1 if the name of someNPC has the text "Guard" anywhere within it (so among the names that will be matched are "Guard", "Hlaalu Guard", "Imperial Guard Sharpshooter", and even "CoolGuardsman").

Example 2: case insensitivity (ignore letter case) and more[edit]

This example set shows how to use case insensitivity, word matching and alternation (essentially an OR operator).

Changing the previous example to only match whole words (so it no longer matches "Guardsman") looks like this:

setx match to xStringMatch name "\<Guard\>"

You can leave out the \> part and it will then be able to match "Guardsman", but still won't match "CoolGuardsman".

By default, all matches are case sensitive. As detailed in the quick summary above, this can be turned off via a special expression:

setx match to xStringMatch name "(?i)Guard" ;this will match all case variations, e.g.: guard, Guard, GuArd, GUARD...
setx match to xStringMatch name "(?i:G)uard" ;this will only match "guard" or "Guard".

The alternation operator | can be used to match either one of its two sides (it's essentially an OR operator). Here are the previous examples modified to match Ordinators as well:

setx match to xStringMatch name "(?i)Guard|Ordinator" ;case insensitive
setx match to xStringMatch name "(?i)\<(guard|ordinator)\>" ;case insensitive, word matching

Example 3: detect guildhalls[edit]

The cell names of Mages Guild and Fighters Guild halls vary according to the city they are in, and in Wolverine Hall they use a different format. The example script below will display a message whenever the player enters any MG or FG guildhall. It will also detect the cell change no matter how it occurs, so it will detect any kind of player teleportation and transportation just fine, including guild guide and scripting/console commands (such as coc).

begin matchtest
;When the PC enters a new cell, it will check to see if it
;is either a Mage's or Fighter's guildhall.  If so, the PC will be greeted.
short framecount
long prev_cell
long cell
long temp

;don't run every frame
set framecount to ( framecount + 1 )
if ( framecount < 20 )
        return
endif
set framecount to 0

setx cell to xPCCellID
ifx ( prev_cell ) ;on first execution it'll be 0  (for completeness, you can add game reload detection here to handle saving and loading)
else
        set prev_cell to cell
endif
setx temp to xStringCompare cell prev_cell ;temp will be 0 when the current cell name and previous cell name are identical
ifx ( temp ) ;this is a more reliable alternative to the CellChanged function. it will also be true one time and can be checked only once in a while if desired (as opposed to every frame).
        set prev_cell to cell
        setx temp to xStringMatch cell ", Guild of (Fighters|Mages)$|(Fighter|Mage)'s Guild$"
        if ( temp )
                MessageBox "Welcome to the guildhall."
        endif
endif

end

The xStringMatch call in this script can also be written in the following ways - they are all exactly equivalent:

xStringMatch cell "(, Guild of (Fighters|Mages)$)|((Fighter|Mage)'s Guild$)"
xStringMatch cell "(, Guild of (Fighters|Mages)|(Fighter|Mage)'s Guild)$"

Example 4: detect a diverse set of items[edit]

The example script will check the ID of the item (or any other entity) the player's cursor is pointed at, and will trigger a message if it matches any one condition out of a varied condition set. Specifically, it will trigger for the following item IDs:

  • Those that are exactly equal to "sc_paper plain" or "daedric_towershield" or "cup" (the last one should never match, unless a mod adds such an item).
  • Those that begin with "misc_com_".
  • Those that contain "bowl" anywhere.
begin matchtest2

short framecount
long pctarget
long temp
long bid

if ( MenuMode ) ;don't run in menus
        return
endif

;don't run every frame
set framecount to ( framecount + 1 )
if ( framecount < 20 )
        return
endif
set framecount to 0

setx pctarget to xGetPCTarget
ifx ( pctarget )
        setx bid to pctarget->xGetBaseID
        setx temp to xStringMatch bid "^(sc_paper plain|daedric_towershield|cup)$|^misc_com_|bowl"
        ifx ( temp )
                MessageBox "Target id matched."
        endif
endif

end

Example 5: wildcards[edit]

In a regular expression, the . special character will match any one character. Similarly, there are symbols that function as less general wildcards (for instance they match any digit, any lowercase letter or any word). Combined with the quantifiers *, +, ? and {}, they can be turned into wildcards that match multiple or any number of characters. An optional wildcard that matches any number of characters is created by the .* symbol.

xStringMatch armorid "bonemold.*_helm" ;matches any bonemold helm (that are 5 different IDs/helms in the game)
xStringMatch armorid "netch_leather.*_helm" ;same as above. anything or nothing can go in place of .* so both "netch_leather_helm" and "netch_leather_boiled_helm" will be matched.
xStringMatch armorid "iron_.+_left" ;will match all left-side iron armor pieces, but will NOT match the string "iron__left".

Note the above examples will match even if armorid is only a partial match (e.g. the last example will match if armorid is "blackiron_pauldron_left_special"), for an exact match add ^ at the beginning of the expression and $ at its end (like in example 4). Note that wildcards aren't the answer to everything in a regular expression, sometimes you may want to use alternation instead, and for example, if you wanted to match filenames ending with ".txt" you would not use the anything-wildcard symbol (like you'd do in a simple Windows file search), you'd just do it like this, with no wildcard:

xStringMatch fname "\.txt$" ;match any txt file
xStringMatch fname "\.es(p|m)$" ;match any esm and esp files

Don't forget that all of the examples shown above are case sensitive, as that is the default!

See Also[edit]

xPCCellID
xRefID
xMyCellID
xGetName
xSetName
xGetBaseID
xStringCompare
xStringLength
xStringParse
xStringBuild
xLogMessage
xMessageFix