c# – Regex series of optional matches with separator depending on state

You could use the following approach:

  1. Split the input string on the delimiter - (as @Barmar suggests in their comment to the question post)
  2. Then, for each part of the split input string:
    1. Use regex to verify that the part starts with an integer
    2. Verify that the part ends with a valid unit (directly following the integer)
    3. Verify that the unit ‘size’ is smaller than the preceding unit ‘size’

To check whether the part ends with a valid unit (step 2.2), you can use regex to remove the initial integer part, and then try to find the unit in a dictionary that only contains your valid units. (I am recommending a dictionary rather than a hash set due to step 2.3.)

To check whether the unit ‘size’ is smaller than the preceding unit ‘size’ (step 2.3), the dictionary containing your valid units could have a ‘comparison size’ as the value for the unit (key). E.g. the larger the physical unit, the larger the ‘comparison size’ ("gal": 4, "qt": 3, and so on).


An implementation of this approach could be:

private const char Delimiter="-";

private static readonly Regex StartsWithIntegerRegex = new Regex(@"^(\d+)");

private static readonly Dictionary<string, int> ComparisonSizeByUnit = new()
{
    ["oz"] = 0,
    ["cup"] = 1,
    ["pt"] = 2,
    ["qt"] = 3,
    ["gal"] = 4,
};
private static bool IsValidQuantityInput(string quantityInput)
{
    if (string.IsNullOrWhiteSpace(quantityInput))
    {
        return false;
    }

    var quantities = quantityInput.Split(Delimiter);
    
    if (!TryParseQuantity(quantities[0], out var previousUnitComparisonSize))
    {
        return false;
    }
    
    for (var i = 1; i < quantities.Length; i++)
    {
        if (!TryParseQuantity(quantities[i], out var unitComparisonSize))
        {
            return false;
        }
        
        if (unitComparisonSize >= previousUnitComparisonSize)
        {
            return false;
        }
        
        previousUnitComparisonSize = unitComparisonSize;
    }
    
    return true;
}
private static bool TryParseQuantity(string quantity, out int unitComparisonSize)
{
    var quantityStartsWithInteger = StartsWithIntegerRegex.IsMatch(quantity);
    
    if (!quantityStartsWithInteger)
    {
        unitComparisonSize = 0;
        return false;
    }
    
    var unit = StartsWithIntegerRegex.Replace(quantity, string.Empty);
    
    return ComparisonSizeByUnit.TryGetValue(unit, out unitComparisonSize);
}

Read more here: Source link