uCalc API Version: 2.1.3-preview.2 Released: 6/17/2026

Warning

uCalc API Preview Release Notice:The documentation describes the intended behavior of the API. The current preview build contains incomplete features, unoptimized performance, and is subject to breaking changes.

StopAfter = [int]

Property

Product: 

Transformer Library

Class: 

Rule

Gets or sets the maximum number of matches a rule will find and keep, ignoring all subsequent matches for that rule.

Remarks

🎯 Limiting Matches with StopAfter

The @StopAfter property sets a limit on the number of matches a rule can find. Once the specified number of matches has been found, the Transformer stops searching for any more matches for that specific rule. This is an efficient way to limit results when you only need the first few occurrences of a pattern.

The default value is -1 (or the maximum value for an unsigned integer), which signifies no limit.

⚙️ Getter and Setter Behavior

This property functions as both a getter and a setter:

  • Getter: var limit = myRule.StopAfter;Returns the current match limit for the rule.

  • Setter: myRule.StopAfter = 5; or SetStopAfter() method.Sets the match limit. This method supports a fluent interface, returning the Rule object to allow for method chaining.

StopAfter vs. StartAfter vs. Maximum

Choosing the correct property to limit matches is crucial. This table clarifies their distinct behaviors:

PropertyBehaviorUse Case
@StopAfter(n) (This Property)Keeps the first n matches found and ignores all subsequent ones.Getting the first few results of a search ("take first n").
@StartAfter(n)Skips the first n matches found and keeps all subsequent ones.Skipping a known header or preamble ("skip first n").
@Maximum(n)Validates. If more than n matches are found, all matches for this rule are invalidated and discarded.Enforcing a "max N allowed" structural rule. Fails if the limit is exceeded.
@GlobalMaximum(n)Validates globally. If more than n matches are found, all matches for all rules are invalidated.Failing the entire transform if a critical structural rule is violated.

You can combine @StartAfter and @StopAfter to retrieve a "page" of results. The @StopAfter limit is applied first during the scan, and then @StartAfter filters that result set. For example, @StartAfter(10).@StopAfter(20) will find the first 20 matches, then discard the first 10, resulting in matches 11 through 20.

💡 Why uCalc? (Comparative Analysis)

In a standard data processing workflow, like using LINQ in C#, you would achieve this imperatively after collecting all results:

// C# LINQ examplevar firstFiveResults = allMatches.Take(5);

uCalc's @StopAfter is declarative and can be more performant. You state the limit directly on the rule, and the engine can often stop its search early once the limit is reached, avoiding the work of finding all possible matches in a large document. This makes it a highly efficient tool for processing large-scale text.

Examples

Finds and transforms only the first three occurrences of a pattern, ignoring any subsequent ones.
				
					using uCalcSoftware;

var uc = new uCalc();
var t = uc.NewTransformer();
t.Text = "a b c a d e a f g a h i";
var ruleA = t.FromTo("a", "[A]");

// Only find and transform the first 3 occurrences of 'a'.
ruleA.StopAfter = 3;

Console.WriteLine(t.Transform());
				
			
[A] b c [A] d e [A] f g a h i
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

int main() {
   uCalc uc;
   auto t = uc.NewTransformer();
   t.Text("a b c a d e a f g a h i");
   auto ruleA = t.FromTo("a", "[A]");

   // Only find and transform the first 3 occurrences of 'a'.
   ruleA.StopAfter(3);

   cout << t.Transform() << endl;
}
				
			
[A] b c [A] d e [A] f g a h i
				
					Imports System
Imports uCalcSoftware
Public Module Program
   Public Sub Main()
      Dim uc As New uCalc()
      Dim t = uc.NewTransformer()
      t.Text = "a b c a d e a f g a h i"
      Dim ruleA = t.FromTo("a", "[A]")
      
      '// Only find and transform the first 3 occurrences of 'a'.
      ruleA.StopAfter = 3
      
      Console.WriteLine(t.Transform())
   End Sub
End Module
				
			
[A] b c [A] d e [A] f g a h i
Processes a log file but stops after finding the first error message to focus on the initial problem.
				
					using uCalcSoftware;

var uc = new uCalc();
var t = uc.NewTransformer();
var logText = "ERROR: Fail 1. INFO: OK. ERROR: Fail 2. ERROR: Fail 3.";
t.Text = logText;

var errorRule = t.Pattern("ERROR: {msg}.");
// Stop after finding the first error to focus on the initial problem.
errorRule.StopAfter = 1;
t.Find();

Console.WriteLine($"First error found: {t.Matches.Text}");
				
			
First error found: ERROR: Fail 1.
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

int main() {
   uCalc uc;
   auto t = uc.NewTransformer();
   auto logText = "ERROR: Fail 1. INFO: OK. ERROR: Fail 2. ERROR: Fail 3.";
   t.Text(logText);

   auto errorRule = t.Pattern("ERROR: {msg}.");
   // Stop after finding the first error to focus on the initial problem.
   errorRule.StopAfter(1);
   t.Find();

   cout << "First error found: " << t.Matches().Text() << endl;
}
				
			
First error found: ERROR: Fail 1.
				
					Imports System
Imports uCalcSoftware
Public Module Program
   Public Sub Main()
      Dim uc As New uCalc()
      Dim t = uc.NewTransformer()
      Dim logText = "ERROR: Fail 1. INFO: OK. ERROR: Fail 2. ERROR: Fail 3."
      t.Text = logText
      
      Dim errorRule = t.Pattern("ERROR: {msg}.")
      '// Stop after finding the first error to focus on the initial problem.
      errorRule.StopAfter = 1
      t.Find()
      
      Console.WriteLine($"First error found: {t.Matches.Text}")
   End Sub
End Module
				
			
First error found: ERROR: Fail 1.
Internal Test: Combines `@StartAfter` and `@StopAfter` to retrieve a specific 'page' of results (matches 3 through 7).
				
					using uCalcSoftware;

var uc = new uCalc();
var t = uc.NewTransformer();
t.Text = "1 2 3 4 5 6 7 8 9 10 11 12";
var rule = t.Pattern("{@Number}");

// Get a "page" of results: matches 3 through 7.
// The engine will stop finding numbers after the 7th match is found.
// Then, it will skip the first 2 matches from that set.
rule.StartAfter = 2; // Skip first 2
rule.StopAfter = 7;  // Find up to 7

t.Find();

Console.WriteLine("Matches found:");
Console.WriteLine(t.Matches.Text);
				
			
Matches found:
3
4
5
6
7
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

int main() {
   uCalc uc;
   auto t = uc.NewTransformer();
   t.Text("1 2 3 4 5 6 7 8 9 10 11 12");
   auto rule = t.Pattern("{@Number}");

   // Get a "page" of results: matches 3 through 7.
   // The engine will stop finding numbers after the 7th match is found.
   // Then, it will skip the first 2 matches from that set.
   rule.StartAfter(2); // Skip first 2
   rule.StopAfter(7);  // Find up to 7

   t.Find();

   cout << "Matches found:" << endl;
   cout << t.Matches().Text() << endl;
}
				
			
Matches found:
3
4
5
6
7
				
					Imports System
Imports uCalcSoftware
Public Module Program
   Public Sub Main()
      Dim uc As New uCalc()
      Dim t = uc.NewTransformer()
      t.Text = "1 2 3 4 5 6 7 8 9 10 11 12"
      Dim rule = t.Pattern("{@Number}")
      
      '// Get a "page" of results: matches 3 through 7.
      '// The engine will stop finding numbers after the 7th match is found.
      '// Then, it will skip the first 2 matches from that set.
      rule.StartAfter = 2 '// Skip first 2
      rule.StopAfter = 7  '// Find up to 7
      
      t.Find()
      
      Console.WriteLine("Matches found:")
      Console.WriteLine(t.Matches.Text)
   End Sub
End Module
				
			
Matches found:
3
4
5
6
7
Rule StartAfter()
				
					using uCalcSoftware;

var uc = new uCalc();
var t = uc.NewTransformer();
var FruitsXML =
"""

<Fruits>
  <Fruit CommonName='Apple' ScientificName='Malus domestica' />
  <Fruit CommonName='Banana' ScientificName='Musa acuminata' />
  <Fruit CommonName='Orange' ScientificName='Citrus × sinensis' />
  <Fruit CommonName='Grapes' ScientificName='Vitis vinifera' />
  <Fruit CommonName='Strawberry' ScientificName='Fragaria × ananassa' />
  <Fruit CommonName='Pineapple' ScientificName='Ananas comosus' />
  <Fruit CommonName='Mango' ScientificName='Mangifera indica' />
  <Fruit CommonName='Blueberry' ScientificName='Vaccinium corymbosum' />
  <Fruit CommonName='Rambutan' ScientificName='Nephelium lappaceum' />
  <Fruit CommonName='Salak (Snake Fruit)' ScientificName='Salacca zalacca' />
  <Fruit CommonName='Jabuticaba' ScientificName='Plinia cauliflora' />
  <Fruit CommonName='Watermelon' ScientificName='Citrullus lanatus' />
</Fruits>

""";

var Fruit = t.FromTo("CommonName={@string:name}", "{name}");

// StopAfter()
Fruit.StopAfter = 4;
t.Filter(FruitsXML);
Console.WriteLine($"*** Stop after: {Fruit.StopAfter} ***");
Console.WriteLine(t.Matches.Text);
Fruit.StopAfter = -1; // Resets back to infinity (default) for next example
Console.WriteLine("");

// StartAfter()
Fruit.StartAfter = 6;
t.Filter(FruitsXML);
Console.WriteLine($"*** Start after: {Fruit.StartAfter} ***");
Console.WriteLine(t.Matches.Text);
Fruit.StartAfter = 0; // Resets back to 0 (default) for next example
Console.WriteLine("");


// Both StartAfter() and StopAfter()
Fruit.SetStartAfter(2).SetStopAfter(5);
t.Filter(FruitsXML);
Console.WriteLine($"*** Between {Fruit.StartAfter + 1} and {Fruit.StopAfter} ***");
Console.WriteLine(t.Matches.Text);
Console.WriteLine("");

// All
uc.DefineVariable("x = 1");
Fruit = t.FromTo("CommonName={@string:name}", "{@Eval: x++}. {name}");
t.Filter(FruitsXML);
Console.WriteLine("*** All ***");
Console.WriteLine(t.Matches.Text);
				
			
*** Stop after: 4 ***
Apple
Banana
Orange
Grapes

*** Start after: 6 ***
Mango
Blueberry
Rambutan
Salak (Snake Fruit)
Jabuticaba
Watermelon

*** Between 3 and 5 ***
Orange
Grapes
Strawberry

*** All ***
1. Apple
2. Banana
3. Orange
4. Grapes
5. Strawberry
6. Pineapple
7. Mango
8. Blueberry
9. Rambutan
10. Salak (Snake Fruit)
11. Jabuticaba
12. Watermelon
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

int main() {
   uCalc uc;
   auto t = uc.NewTransformer();
   auto FruitsXML =
   R"(
<Fruits>
  <Fruit CommonName='Apple' ScientificName='Malus domestica' />
  <Fruit CommonName='Banana' ScientificName='Musa acuminata' />
  <Fruit CommonName='Orange' ScientificName='Citrus × sinensis' />
  <Fruit CommonName='Grapes' ScientificName='Vitis vinifera' />
  <Fruit CommonName='Strawberry' ScientificName='Fragaria × ananassa' />
  <Fruit CommonName='Pineapple' ScientificName='Ananas comosus' />
  <Fruit CommonName='Mango' ScientificName='Mangifera indica' />
  <Fruit CommonName='Blueberry' ScientificName='Vaccinium corymbosum' />
  <Fruit CommonName='Rambutan' ScientificName='Nephelium lappaceum' />
  <Fruit CommonName='Salak (Snake Fruit)' ScientificName='Salacca zalacca' />
  <Fruit CommonName='Jabuticaba' ScientificName='Plinia cauliflora' />
  <Fruit CommonName='Watermelon' ScientificName='Citrullus lanatus' />
</Fruits>
)";

   auto Fruit = t.FromTo("CommonName={@string:name}", "{name}");

   // StopAfter()
   Fruit.StopAfter(4);
   t.Filter(FruitsXML);
   cout << "*** Stop after: " << Fruit.StopAfter() << " ***" << endl;
   cout << t.Matches().Text() << endl;
   Fruit.StopAfter(-1); // Resets back to infinity (default) for next example
   cout << "" << endl;

   // StartAfter()
   Fruit.StartAfter(6);
   t.Filter(FruitsXML);
   cout << "*** Start after: " << Fruit.StartAfter() << " ***" << endl;
   cout << t.Matches().Text() << endl;
   Fruit.StartAfter(0); // Resets back to 0 (default) for next example
   cout << "" << endl;


   // Both StartAfter() and StopAfter()
   Fruit.SetStartAfter(2).SetStopAfter(5);
   t.Filter(FruitsXML);
   cout << "*** Between " << Fruit.StartAfter() + 1 << " and " << Fruit.StopAfter() << " ***" << endl;
   cout << t.Matches().Text() << endl;
   cout << "" << endl;

   // All
   uc.DefineVariable("x = 1");
   Fruit = t.FromTo("CommonName={@string:name}", "{@Eval: x++}. {name}");
   t.Filter(FruitsXML);
   cout << "*** All ***" << endl;
   cout << t.Matches().Text() << endl;
}
				
			
*** Stop after: 4 ***
Apple
Banana
Orange
Grapes

*** Start after: 6 ***
Mango
Blueberry
Rambutan
Salak (Snake Fruit)
Jabuticaba
Watermelon

*** Between 3 and 5 ***
Orange
Grapes
Strawberry

*** All ***
1. Apple
2. Banana
3. Orange
4. Grapes
5. Strawberry
6. Pineapple
7. Mango
8. Blueberry
9. Rambutan
10. Salak (Snake Fruit)
11. Jabuticaba
12. Watermelon
				
					Imports System
Imports uCalcSoftware
Public Module Program
   Public Sub Main()
      Dim uc As New uCalc()
      Dim t = uc.NewTransformer()
      Dim FruitsXML =
      "
<Fruits>
  <Fruit CommonName='Apple' ScientificName='Malus domestica' />
  <Fruit CommonName='Banana' ScientificName='Musa acuminata' />
  <Fruit CommonName='Orange' ScientificName='Citrus × sinensis' />
  <Fruit CommonName='Grapes' ScientificName='Vitis vinifera' />
  <Fruit CommonName='Strawberry' ScientificName='Fragaria × ananassa' />
  <Fruit CommonName='Pineapple' ScientificName='Ananas comosus' />
  <Fruit CommonName='Mango' ScientificName='Mangifera indica' />
  <Fruit CommonName='Blueberry' ScientificName='Vaccinium corymbosum' />
  <Fruit CommonName='Rambutan' ScientificName='Nephelium lappaceum' />
  <Fruit CommonName='Salak (Snake Fruit)' ScientificName='Salacca zalacca' />
  <Fruit CommonName='Jabuticaba' ScientificName='Plinia cauliflora' />
  <Fruit CommonName='Watermelon' ScientificName='Citrullus lanatus' />
</Fruits>
"
      
      Dim Fruit = t.FromTo("CommonName={@string:name}", "{name}")
      
      '// StopAfter()
      Fruit.StopAfter = 4
      t.Filter(FruitsXML)
      Console.WriteLine($"*** Stop after: {Fruit.StopAfter} ***")
      Console.WriteLine(t.Matches.Text)
      Fruit.StopAfter = -1 '// Resets back to infinity (default) for next example
      Console.WriteLine("")
      
      '// StartAfter()
      Fruit.StartAfter = 6
      t.Filter(FruitsXML)
      Console.WriteLine($"*** Start after: {Fruit.StartAfter} ***")
      Console.WriteLine(t.Matches.Text)
      Fruit.StartAfter = 0 '// Resets back to 0 (default) for next example
      Console.WriteLine("")
      
      
      '// Both StartAfter() and StopAfter()
      Fruit.SetStartAfter(2).SetStopAfter(5)
      t.Filter(FruitsXML)
      Console.WriteLine($"*** Between {Fruit.StartAfter + 1} and {Fruit.StopAfter} ***")
      Console.WriteLine(t.Matches.Text)
      Console.WriteLine("")
      
      '// All
      uc.DefineVariable("x = 1")
      Fruit = t.FromTo("CommonName={@string:name}", "{@Eval: x++}. {name}")
      t.Filter(FruitsXML)
      Console.WriteLine("*** All ***")
      Console.WriteLine(t.Matches.Text)
   End Sub
End Module
				
			
*** Stop after: 4 ***
Apple
Banana
Orange
Grapes

*** Start after: 6 ***
Mango
Blueberry
Rambutan
Salak (Snake Fruit)
Jabuticaba
Watermelon

*** Between 3 and 5 ***
Orange
Grapes
Strawberry

*** All ***
1. Apple
2. Banana
3. Orange
4. Grapes
5. Strawberry
6. Pineapple
7. Mango
8. Blueberry
9. Rambutan
10. Salak (Snake Fruit)
11. Jabuticaba
12. Watermelon