uCalc API Version: 2.1.3-preview.2 Released: 6/16/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.
Advanced Data Validation with the Transformer
Product:
Class:
Learn how to use the Transformer to enforce structural rules, validate data formats, and ensure content integrity using advanced pattern properties.
Remarks
🛡️ Advanced Data Validation with the Transformer
The Transformer is not just a tool for finding and replacing text; it's also a powerful engine for validating the structure and content of your data. By defining rules that describe what a valid document should look like, you can create sophisticated linters, configuration file validators, or static analysis tools.
This tutorial moves beyond simple pattern matching to explore the features that allow you to enforce constraints, check values, and ensure the integrity of your text data.
1. ✅ Existence and Occurrence Checks
The most basic form of validation is ensuring that a required pattern exists or that it appears a specific number of times. The Transformer provides both imperative and declarative ways to handle this.
The Imperative Way: Find() and Count()
The simplest approach is to define a Pattern and then check the result of the Matches collection after a Find() operation.
using (var t = new uCalc.Transformer()) { t.Pattern("Header: OK"); if (t.Find("Header: OK").Matches.Count() > 0) { Console.WriteLine("Document is valid."); }}While functional, this requires you to write explicit checking logic in your application code for every rule.
The Declarative Way: Minimum and Maximum
A more powerful and declarative approach is to use the Minimum and Maximum properties on a Rule object. These properties instruct the engine to automatically invalidate a rule's matches if the count is not met.
- Minimum(n): The rule must match at least
ntimes for its matches to be considered valid. If it finds fewer, its own matches are discarded (other rules are unaffected). - Maximum(n): The rule must match at most
ntimes. If it finds more, its own matches are discarded. - GlobalMinimum(n) / GlobalMaximum(n): Same as above, but if the condition is not met, the entire transformation fails, and no matches from any rule are returned.
This allows you to build validation logic directly into your patterns, as shown in the practical example below.
2. 🔬 Content Validation with {@Eval}
Sometimes, it's not enough to know that a pattern exists; you also need to validate its content. For example, a port number must not only be a number, but it must be within a specific range. The {@Eval} directive is the perfect tool for this.
You can create a rule that captures a value and then uses {@Eval} with a conditional function like IIf to perform a check during the transformation.
// This rule will validate port numbers as it transforms them.t.FromTo("Port: {@Number:port_num}", "Port: {port_num} - {@Eval: IIf(Double(port_num) > 0 and Double(port_num) < 65536, 'OK', 'INVALID')}");This turns the transformer into a dynamic validation engine that can check values against business logic, not just syntax.
3. ❌ Negative Validation
Validation can also mean ensuring that certain patterns do not exist. You can achieve this in two ways:
- Define a pattern for the invalid syntax and then check if its
Matches.Count()is greater than zero. If it is, the document is invalid. - Use SkipOver for sections that are allowed but should not be processed by other validation rules. This is useful for ignoring comments or other non-functional blocks.
Examples
Succinct: Checks for the existence of a required header in a string.
using uCalcSoftware;
var uc = new uCalc();
using (var t = new uCalc.Transformer()) {
var text_ok = "Header: OK";
var text_fail = "Header: ERROR";
// This rule only matches if the status is "OK"
t.Pattern("Header: OK");
// Find() returns the transformer, so we can chain Matches().Count()
if (t.SetText(text_ok).Find().Matches.Count() > 0) {
Console.WriteLine("text_ok is valid.");
}
if (t.SetText(text_fail).Find().Matches.Count() == 0) {
Console.WriteLine("text_fail is invalid.");
}
}
text_ok is valid.
text_fail is invalid. using uCalcSoftware; var uc = new uCalc(); using (var t = new uCalc.Transformer()) { var text_ok = "Header: OK"; var text_fail = "Header: ERROR"; // This rule only matches if the status is "OK" t.Pattern("Header: OK"); // Find() returns the transformer, so we can chain Matches().Count() if (t.SetText(text_ok).Find().Matches.Count() > 0) { Console.WriteLine("text_ok is valid."); } if (t.SetText(text_fail).Find().Matches.Count() == 0) { Console.WriteLine("text_fail is invalid."); } }
#include
#include "uCalc.h"
using namespace std;
using namespace uCalcSoftware;
int main() {
uCalc uc;
{
uCalc::Transformer t;
t.Owned(); // Causes t to be released when it goes out of scope
auto text_ok = "Header: OK";
auto text_fail = "Header: ERROR";
// This rule only matches if the status is "OK"
t.Pattern("Header: OK");
// Find() returns the transformer, so we can chain Matches().Count()
if (t.SetText(text_ok).Find().Matches().Count() > 0) {
cout << "text_ok is valid." << endl;
}
if (t.SetText(text_fail).Find().Matches().Count() == 0) {
cout << "text_fail is invalid." << endl;
}
}
}
text_ok is valid.
text_fail is invalid. #include <iostream> #include "uCalc.h" using namespace std; using namespace uCalcSoftware; int main() { uCalc uc; { uCalc::Transformer t; t.Owned(); // Causes t to be released when it goes out of scope auto text_ok = "Header: OK"; auto text_fail = "Header: ERROR"; // This rule only matches if the status is "OK" t.Pattern("Header: OK"); // Find() returns the transformer, so we can chain Matches().Count() if (t.SetText(text_ok).Find().Matches().Count() > 0) { cout << "text_ok is valid." << endl; } if (t.SetText(text_fail).Find().Matches().Count() == 0) { cout << "text_fail is invalid." << endl; } } }
Imports System
Imports uCalcSoftware
Public Module Program
Public Sub Main()
Dim uc As New uCalc()
Using t As New uCalc.Transformer()
Dim text_ok = "Header: OK"
Dim text_fail = "Header: ERROR"
'// This rule only matches if the status is "OK"
t.Pattern("Header: OK")
'// Find() returns the transformer, so we can chain Matches().Count()
If t.SetText(text_ok).Find().Matches.Count() > 0 Then
Console.WriteLine("text_ok is valid.")
End If
If t.SetText(text_fail).Find().Matches.Count() = 0 Then
Console.WriteLine("text_fail is invalid.")
End If
End Using
End Sub
End Module
text_ok is valid.
text_fail is invalid. Imports System Imports uCalcSoftware Public Module Program Public Sub Main() Dim uc As New uCalc() Using t As New uCalc.Transformer() Dim text_ok = "Header: OK" Dim text_fail = "Header: ERROR" '// This rule only matches if the status is "OK" t.Pattern("Header: OK") '// Find() returns the transformer, so we can chain Matches().Count() If t.SetText(text_ok).Find().Matches.Count() > 0 Then Console.WriteLine("text_ok is valid.") End If If t.SetText(text_fail).Find().Matches.Count() = 0 Then Console.WriteLine("text_fail is invalid.") End If End Using End Sub End Module
Practical: Validates a configuration file format by enforcing the number of times specific keys must appear.
using uCalcSoftware;
var uc = new uCalc();
using (var validator = new uCalc.Transformer()) {
// Rule 1: Must contain exactly one 'Host' setting.
var hostRule = validator.Pattern("Host: {@Alpha}").SetMinimum(1).SetMaximum(1);
// Rule 2: Must contain at least one 'Port' setting.
var portRule = validator.Pattern("Port: {@Number}").SetMinimum(1);
var configFile_OK = """
Host: server1
Port: 80
Port: 443
""";
var configFile_FAIL = "Port: 80"; // Missing Host
Console.WriteLine("--- Testing Valid Config ---");
validator.SetText(configFile_OK).Find();
if (hostRule.Matches.Count() > 0 && portRule.Matches.Count() > 0) {
Console.WriteLine("Config file is valid.");
} else {
Console.WriteLine("Config file is invalid.");
}
Console.WriteLine();
Console.WriteLine("--- Testing Invalid Config ---");
validator.SetText(configFile_FAIL).Find();
// The Minimum(1) on hostRule causes it to have 0 matches if none are found.
if (hostRule.Matches.Count() > 0 && portRule.Matches.Count() > 0) {
Console.WriteLine("Config file is valid.");
} else {
Console.WriteLine("Config file is invalid.");
if (hostRule.Matches.Count() == 0) {
Console.WriteLine($"- Reason: Host rule failed (found {hostRule.Matches.Count()}, expected 1).");
}
}
}
--- Testing Valid Config ---
Config file is valid.
--- Testing Invalid Config ---
Config file is invalid.
- Reason: Host rule failed (found 0, expected 1). using uCalcSoftware; var uc = new uCalc(); using (var validator = new uCalc.Transformer()) { // Rule 1: Must contain exactly one 'Host' setting. var hostRule = validator.Pattern("Host: {@Alpha}").SetMinimum(1).SetMaximum(1); // Rule 2: Must contain at least one 'Port' setting. var portRule = validator.Pattern("Port: {@Number}").SetMinimum(1); var configFile_OK = """ Host: server1 Port: 80 Port: 443 """; var configFile_FAIL = "Port: 80"; // Missing Host Console.WriteLine("--- Testing Valid Config ---"); validator.SetText(configFile_OK).Find(); if (hostRule.Matches.Count() > 0 && portRule.Matches.Count() > 0) { Console.WriteLine("Config file is valid."); } else { Console.WriteLine("Config file is invalid."); } Console.WriteLine(); Console.WriteLine("--- Testing Invalid Config ---"); validator.SetText(configFile_FAIL).Find(); // The Minimum(1) on hostRule causes it to have 0 matches if none are found. if (hostRule.Matches.Count() > 0 && portRule.Matches.Count() > 0) { Console.WriteLine("Config file is valid."); } else { Console.WriteLine("Config file is invalid."); if (hostRule.Matches.Count() == 0) { Console.WriteLine($"- Reason: Host rule failed (found {hostRule.Matches.Count()}, expected 1)."); } } }
#include
#include "uCalc.h"
using namespace std;
using namespace uCalcSoftware;
int main() {
uCalc uc;
{
uCalc::Transformer validator;
validator.Owned(); // Causes validator to be released when it goes out of scope
// Rule 1: Must contain exactly one 'Host' setting.
auto hostRule = validator.Pattern("Host: {@Alpha}").SetMinimum(1).SetMaximum(1);
// Rule 2: Must contain at least one 'Port' setting.
auto portRule = validator.Pattern("Port: {@Number}").SetMinimum(1);
auto configFile_OK = R"(
Host: server1
Port: 80
Port: 443
)";
auto configFile_FAIL = "Port: 80"; // Missing Host
cout << "--- Testing Valid Config ---" << endl;
validator.SetText(configFile_OK).Find();
if (hostRule.Matches().Count() > 0 && portRule.Matches().Count() > 0) {
cout << "Config file is valid." << endl;
} else {
cout << "Config file is invalid." << endl;
}
cout << endl;
cout << "--- Testing Invalid Config ---" << endl;
validator.SetText(configFile_FAIL).Find();
// The Minimum(1) on hostRule causes it to have 0 matches if none are found.
if (hostRule.Matches().Count() > 0 && portRule.Matches().Count() > 0) {
cout << "Config file is valid." << endl;
} else {
cout << "Config file is invalid." << endl;
if (hostRule.Matches().Count() == 0) {
cout << "- Reason: Host rule failed (found " << hostRule.Matches().Count() << ", expected 1)." << endl;
}
}
}
}
--- Testing Valid Config ---
Config file is valid.
--- Testing Invalid Config ---
Config file is invalid.
- Reason: Host rule failed (found 0, expected 1). #include <iostream> #include "uCalc.h" using namespace std; using namespace uCalcSoftware; int main() { uCalc uc; { uCalc::Transformer validator; validator.Owned(); // Causes validator to be released when it goes out of scope // Rule 1: Must contain exactly one 'Host' setting. auto hostRule = validator.Pattern("Host: {@Alpha}").SetMinimum(1).SetMaximum(1); // Rule 2: Must contain at least one 'Port' setting. auto portRule = validator.Pattern("Port: {@Number}").SetMinimum(1); auto configFile_OK = R"( Host: server1 Port: 80 Port: 443 )"; auto configFile_FAIL = "Port: 80"; // Missing Host cout << "--- Testing Valid Config ---" << endl; validator.SetText(configFile_OK).Find(); if (hostRule.Matches().Count() > 0 && portRule.Matches().Count() > 0) { cout << "Config file is valid." << endl; } else { cout << "Config file is invalid." << endl; } cout << endl; cout << "--- Testing Invalid Config ---" << endl; validator.SetText(configFile_FAIL).Find(); // The Minimum(1) on hostRule causes it to have 0 matches if none are found. if (hostRule.Matches().Count() > 0 && portRule.Matches().Count() > 0) { cout << "Config file is valid." << endl; } else { cout << "Config file is invalid." << endl; if (hostRule.Matches().Count() == 0) { cout << "- Reason: Host rule failed (found " << hostRule.Matches().Count() << ", expected 1)." << endl; } } } }
Imports System
Imports uCalcSoftware
Public Module Program
Public Sub Main()
Dim uc As New uCalc()
Using validator As New uCalc.Transformer()
'// Rule 1: Must contain exactly one 'Host' setting.
Dim hostRule = validator.Pattern("Host: {@Alpha}").SetMinimum(1).SetMaximum(1)
'// Rule 2: Must contain at least one 'Port' setting.
Dim portRule = validator.Pattern("Port: {@Number}").SetMinimum(1)
Dim configFile_OK = "
Host: server1
Port: 80
Port: 443
"
Dim configFile_FAIL = "Port: 80" '// Missing Host
Console.WriteLine("--- Testing Valid Config ---")
validator.SetText(configFile_OK).Find()
If hostRule.Matches.Count() > 0 And portRule.Matches.Count() > 0 Then
Console.WriteLine("Config file is valid.")
Else
Console.WriteLine("Config file is invalid.")
End If
Console.WriteLine()
Console.WriteLine("--- Testing Invalid Config ---")
validator.SetText(configFile_FAIL).Find()
'// The Minimum(1) on hostRule causes it to have 0 matches if none are found.
If hostRule.Matches.Count() > 0 And portRule.Matches.Count() > 0 Then
Console.WriteLine("Config file is valid.")
Else
Console.WriteLine("Config file is invalid.")
If hostRule.Matches.Count() = 0 Then
Console.WriteLine($"- Reason: Host rule failed (found {hostRule.Matches.Count()}, expected 1).")
End If
End If
End Using
End Sub
End Module
--- Testing Valid Config ---
Config file is valid.
--- Testing Invalid Config ---
Config file is invalid.
- Reason: Host rule failed (found 0, expected 1). Imports System Imports uCalcSoftware Public Module Program Public Sub Main() Dim uc As New uCalc() Using validator As New uCalc.Transformer() '// Rule 1: Must contain exactly one 'Host' setting. Dim hostRule = validator.Pattern("Host: {@Alpha}").SetMinimum(1).SetMaximum(1) '// Rule 2: Must contain at least one 'Port' setting. Dim portRule = validator.Pattern("Port: {@Number}").SetMinimum(1) Dim configFile_OK = " Host: server1 Port: 80 Port: 443 " Dim configFile_FAIL = "Port: 80" '// Missing Host Console.WriteLine("--- Testing Valid Config ---") validator.SetText(configFile_OK).Find() If hostRule.Matches.Count() > 0 And portRule.Matches.Count() > 0 Then Console.WriteLine("Config file is valid.") Else Console.WriteLine("Config file is invalid.") End If Console.WriteLine() Console.WriteLine("--- Testing Invalid Config ---") validator.SetText(configFile_FAIL).Find() '// The Minimum(1) on hostRule causes it to have 0 matches if none are found. If hostRule.Matches.Count() > 0 And portRule.Matches.Count() > 0 Then Console.WriteLine("Config file is valid.") Else Console.WriteLine("Config file is invalid.") If hostRule.Matches.Count() = 0 Then Console.WriteLine($"- Reason: Host rule failed (found {hostRule.Matches.Count()}, expected 1).") End If End If End Using End Sub End Module