getGroup: Difference between revisions

From RPTools Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
 
(13 intermediate revisions by 4 users not shown)
Line 5: Line 5:


|usage=
|usage=
<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
getGroup(id, match, group)
getGroup(id, match, group)
</source>
</syntaxhighlight>
Where  
Where  
{{param|id|is the id returned by [[strfind|strfind()]]}}
{{param|id|is the id returned by [[strfind|strfind()]]}}
Line 14: Line 14:


|example=
|example=
<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[h: id = strfind("this is a test", "(\\S+)\\s(\\S+)\\s*")]
[h: id = strfind("this is a test", "(\\S+)\\s(\\S+)\\s*")]
match 1, group 0 = [getGroup(id, 1, 0)]<br>
match 1, group 0 = [getGroup(id, 1, 0)]<br>
Line 22: Line 22:
match 2, group 1 = [getGroup(id, 2, 1)]<br>
match 2, group 1 = [getGroup(id, 2, 1)]<br>
match 2, group 2 = [getGroup(id, 2, 2)]<br>
match 2, group 2 = [getGroup(id, 2, 2)]<br>
</source>
</syntaxhighlight>
Returns:
Returns:
<pre>
<pre>
Line 35: Line 35:
'''Example explained'''
'''Example explained'''


First off, normally you only need one {{code|\}} in a regex statement, but because MT uses regex itself and the statement is preparsed you need to double escape it, so {{code|\\}}.
First off, escapes = "\" are used to let the character in question NOT be what it usually is. E.g. "d" is the alphabetical character "d"; "\d" however is thus NOT "d" and with that it gets a 'regex' meaning, in this case 'digit', so 1,2,3,4,5,6,7,8,9 or 0. The same the other way round, e.g. "." means "any character" if you actually want to find a "." (dot) in the text you thus use \. so its NOT the regex "any character" but just a ".".
 
Now the tricky bit: in maptool ALL escapes ("\") are eaten by the maptool parser UNLESS they are preceded by an escape themselves. This happens BEFORE the regex is parsed by the regex parser. THUS ALL ESCAPES MUST BE ESCAPED !! So in the above examples "\d" becomes "\\d" and "\." becomes "\\.". Really tricky it becomes when you want to find the "\" character. This is a regex symbol hence it needs to be escaped: "\\" but as its in maptool every escape must be escaped so it ultimately becomes "\\\\" !
 
note that alternatively you can use [] ANY character in there will be looked at literally (and separately). So \\. == [.]. Obviously here too are exceptions, but read a regex tutorial for that.
 
So back to the above example:
 
*{{code|S}} = 'everything that is NOT a whitespace'
*{{code|S}} = 'everything that is NOT a whitespace'
*{{code|s}} = 'whitespace'
*{{code|s}} = 'white-space'
*{{code|+}} = '1 or more'
*{{code|+}} = '1 or more'
*{{code|*}} = '0 or more'
*{{code|*}} = '0 or more'
Have a look [http://www.addedbytes.com/download/regular-expressions-cheat-sheet-v2/png/ here] for an overview.
Have a look [http://www.addedbytes.com/download/regular-expressions-cheat-sheet-v2/png/ here] for an overview.


Second important thing to know is that a 'group' is defined by '('parenthesis')': "(group1)(group2)(etc.)", where group0 returns the entire search result.  
Second important thing to know is that a group is defined by {{code|'('}}parenthesis{{code|')'}}: {{code|(group1)(group2)(etc.)}}, where group {{code|'0'}} returns the entire "match" result.  


So {{code|\\S}} means grab the first none-whitespace you encounter, {{code|\\S+}} means grap the first none-whitespace you encounter AND ALL characters after that until you encounter a whitespace.
So {{code|\\S}} means grab the first none-whitespace you encounter, {{code|\\S+}} means grab the first none-whitespace you encounter AND ALL characters after that until you encounter a whitespace.
So the regex statement looks for ''(word)(whitespace)(word)(0 or more whitespace)'', where every 'parenthesized part' is a group. This will deliver 2 matches: {{code|"this is"}} and {{code|"a test"}}. The first match is match 1, the second match 2. Where each match again consists out of 5 groups: 4 sets of parenthesis resulting in group1,2,3 & 4 and group0 returning the entire matched string.
Hence the regex statement looks for  
  ''{{code|(string of non-whitespace chars) whitespace (string of non-whitespace chars) 0 or more whitespaces}}''
apply this to the text example and you get:
  MATCH 1: "(this) (is) " MATCH2: "(a) (code)"
Hence in this example you have 2 MATCHES: Match 1 and Match 2 both consists out of 2 GROUPS: Group 1 and Group 2. Note that Group 0 will return the ENTIRE match.


Within one match there are 1 or more groups:
In summary: a search result can have multiple matches, and each match can consist out of 1 or more groups:
* The first group {{code|'0'}} returns the ENTIRE match.  
* The first group {{code|'0'}} returns the ENTIRE match.  
* Every group after that will return partial matches that are within {{code|()}}.
* Every group after that will return partial matches that are within {{code|()}}.
So group {{code|'1'}} will return the first {{code|(\\S)}} part and group {{code|'2'}} will return the second {{code|(\\S)}} of the regex statement. These are respectively (for the first match): {{code|"this"}} and {{code|"is"}}.


[http://www.gskinner.com/RegExr/ Here a link] to test your regex statements (remember that for this applet you only use one {{code|\}} while in MT you need {{code|\\}}.
[http://www.gskinner.com/RegExr/ Here a link] to test your regex statements (remember that for this applet you only use one {{code|\}} while in MT you need {{code|\\}}.
'''Another Example'''
<syntaxhighlight lang="mtmacro" line>
<!--
  (?i) - case insensative
  (d+) - number of dice (not optional)
  d - dice expression separator, upper or lower case
  (d+) - die sides (not optional)
  Optional:
      space* - space before modifier
      ([+-]*d+)* - roll modifier
      space* - space after modifier
      (w+)* - single word for damage type
     
  Example Groups: "(3)d(6)(+2) (Fire)"
-->
[H: regex = "(?i)(\\d+)d(\\d+) *([+-]*\\d+)* *(\\w+)*"]
[H, while(1), code: {
  <!-- cancel input to break out of loop -->
  [H: abort(input("diceExp|3d6+2 Fire|Enter Dice Expression|TEXT"))]
  [H: id = strfind(diceExp,regex)]
  [H: valid = getFindCount(id)]
  [H, if(valid), code: {
      [H: numDice = getGroup(id,1,1)]
      [H: numSides = getGroup(id,1,2)]
      [H: dieMod = getGroup(id,1,3)]
      [H: dmgType = getGroup(id,1,4)]
     
      [H: output = strformat("Original: %{diceExp}<br>Dice Expression: %{numDice}d%{numSides}")]
      [H, if(! json.isEmpty(dieMod)): output = json.append(output,strformat("Modifier: %{dieMod}"))]
      [H, if(! json.isEmpty(dmgType)): output = json.append(output,strformat("Damage Type: %{dmgType}"))]
  };{
      [H: output = strformat("Invalid Dice Expression: [%{diceExp}]")]
  }]
  [H: broadcast(json.toList(output,"<br>"))]
}]
</syntaxhighlight>


}}
}}
[[Category:String Function]]
[[Category:String Function]]

Latest revision as of 23:59, 14 March 2023

getGroup() Function

Introduced in version 1.3b48
Returns the specified capture group for the specified match that was found using strfind().

Usage

getGroup(id, match, group)

Where

  • id - is the id returned by strfind()
  • match - is the number of the match found by strfind()
  • group - is the number of the capture group found by strfind()

Example

[h: id = strfind("this is a test", "(\\S+)\\s(\\S+)\\s*")]
match 1, group 0 = [getGroup(id, 1, 0)]<br>
match 1, group 1 = [getGroup(id, 1, 1)]<br>
match 1, group 2 = [getGroup(id, 1, 2)]<br>
match 2, group 0 = [getGroup(id, 2, 0)]<br>
match 2, group 1 = [getGroup(id, 2, 1)]<br>
match 2, group 2 = [getGroup(id, 2, 2)]<br>

Returns:

match 1, group 0 = this is
match 1, group 1 = this 
match 1, group 2 = is 
match 2, group 0 = a test
match 2, group 1 = a 
match 2, group 2 = test 

Example explained

First off, escapes = "\" are used to let the character in question NOT be what it usually is. E.g. "d" is the alphabetical character "d"; "\d" however is thus NOT "d" and with that it gets a 'regex' meaning, in this case 'digit', so 1,2,3,4,5,6,7,8,9 or 0. The same the other way round, e.g. "." means "any character" if you actually want to find a "." (dot) in the text you thus use \. so its NOT the regex "any character" but just a ".".

Now the tricky bit: in maptool ALL escapes ("\") are eaten by the maptool parser UNLESS they are preceded by an escape themselves. This happens BEFORE the regex is parsed by the regex parser. THUS ALL ESCAPES MUST BE ESCAPED !! So in the above examples "\d" becomes "\\d" and "\." becomes "\\.". Really tricky it becomes when you want to find the "\" character. This is a regex symbol hence it needs to be escaped: "\\" but as its in maptool every escape must be escaped so it ultimately becomes "\\\\" !

note that alternatively you can use [] ANY character in there will be looked at literally (and separately). So \\. == [.]. Obviously here too are exceptions, but read a regex tutorial for that.

So back to the above example:

  • S = 'everything that is NOT a whitespace'
  • s = 'white-space'
  • + = '1 or more'
  • * = '0 or more'

Have a look here for an overview.

Second important thing to know is that a group is defined by '('parenthesis')': (group1)(group2)(etc.), where group '0' returns the entire "match" result.

So \\S means grab the first none-whitespace you encounter, \\S+ means grab the first none-whitespace you encounter AND ALL characters after that until you encounter a whitespace. Hence the regex statement looks for

 (string of non-whitespace chars) whitespace (string of non-whitespace chars) 0 or more whitespaces

apply this to the text example and you get:

 MATCH 1: "(this) (is) " MATCH2: "(a) (code)"

Hence in this example you have 2 MATCHES: Match 1 and Match 2 both consists out of 2 GROUPS: Group 1 and Group 2. Note that Group 0 will return the ENTIRE match.

In summary: a search result can have multiple matches, and each match can consist out of 1 or more groups:

  • The first group '0' returns the ENTIRE match.
  • Every group after that will return partial matches that are within ().

Here a link to test your regex statements (remember that for this applet you only use one \ while in MT you need \\.


Another Example

<!--
   (?i) - case insensative
   (d+) - number of dice (not optional)
   d - dice expression separator, upper or lower case
   (d+) - die sides (not optional)
   Optional:
      space* - space before modifier
      ([+-]*d+)* - roll modifier
      space* - space after modifier
      (w+)* - single word for damage type
      
   Example Groups: "(3)d(6)(+2) (Fire)"
-->
[H: regex = "(?i)(\\d+)d(\\d+) *([+-]*\\d+)* *(\\w+)*"]
[H, while(1), code: {
   <!-- cancel input to break out of loop -->
   [H: abort(input("diceExp|3d6+2 Fire|Enter Dice Expression|TEXT"))]
   [H: id = strfind(diceExp,regex)]
   [H: valid = getFindCount(id)]
   [H, if(valid), code: {
      [H: numDice = getGroup(id,1,1)]
      [H: numSides = getGroup(id,1,2)]
      [H: dieMod = getGroup(id,1,3)]
      [H: dmgType = getGroup(id,1,4)]
      
      [H: output = strformat("Original: %{diceExp}<br>Dice Expression: %{numDice}d%{numSides}")]
      [H, if(! json.isEmpty(dieMod)): output = json.append(output,strformat("Modifier: %{dieMod}"))]
      [H, if(! json.isEmpty(dmgType)): output = json.append(output,strformat("Damage Type: %{dmgType}"))]
   };{
      [H: output = strformat("Invalid Dice Expression: [%{diceExp}]")]
   }]
   [H: broadcast(json.toList(output,"<br>"))]
}]