diff --git a/EPPlus/FormulaParsing/Excel/Functions/ExcelFunction.cs b/EPPlus/FormulaParsing/Excel/Functions/ExcelFunction.cs index 620ff43c..69a1f0a0 100644 --- a/EPPlus/FormulaParsing/Excel/Functions/ExcelFunction.cs +++ b/EPPlus/FormulaParsing/Excel/Functions/ExcelFunction.cs @@ -176,6 +176,41 @@ protected void ValidateArguments(IEnumerable arguments, int mi return true; }, "Expecting at least {0} arguments", minLength.ToString()); } + + + /// + /// This functions validates that the supplied contains at least + /// (the value of) elements. If one of the arguments is an + /// Excel range the number of cells in + /// that range will be counted as well. + /// + /// + /// + /// + /// + protected void ValidateArguments(IEnumerable arguments, int minLength, out int length) + { + Require.That(arguments).Named("arguments").IsNotNull(); + var nArgs = 0; + ThrowArgumentExceptionIf(() => + { + if (arguments.Any()) + { + foreach (var arg in arguments) + { + nArgs++; + if (arg.IsExcelRange) + { + nArgs += arg.ValueAsRangeInfo.GetNCells(); + } + } + if (nArgs >= minLength) return false; + } + return true; + }, "Expecting at least {0} arguments", minLength.ToString()); + length = nArgs; + } + protected string ArgToAddress(IEnumerable arguments, int index) { return arguments.ElementAt(index).IsExcelRange ? arguments.ElementAt(index).ValueAsRangeInfo.Address.FullAddress : ArgToString(arguments, index); diff --git a/EPPlus/FormulaParsing/Excel/Functions/Text/Substitute.cs b/EPPlus/FormulaParsing/Excel/Functions/Text/Substitute.cs index 605d30fa..2c0bd11b 100644 --- a/EPPlus/FormulaParsing/Excel/Functions/Text/Substitute.cs +++ b/EPPlus/FormulaParsing/Excel/Functions/Text/Substitute.cs @@ -34,12 +34,44 @@ public class Substitute : ExcelFunction { public override CompileResult Execute(IEnumerable arguments, ParsingContext context) { - ValidateArguments(arguments, 3); + int argCount; + ValidateArguments(arguments, 3, out argCount); var text = ArgToString(arguments, 0); var find = ArgToString(arguments, 1); var replaceWith = ArgToString(arguments, 2); - var result = text.Replace(find, replaceWith); + string result; + if (argCount > 3) + { + var instanceNum = ArgToInt(arguments, 3); + result = ReplaceFirst(text, find, replaceWith, instanceNum); + } + else + { + result = text.Replace(find, replaceWith); + } return CreateResult(result, DataType.String); } + + /// + /// Replaces only the Nth instance of substring. + /// + /// String to modify + /// Substring to look for + /// Replacement for the matched substring + /// One-based index of match to replace + /// Modified copy of parameter text where only the specified substring instance has been replaced + private static string ReplaceFirst(string text, string search, string replace, int instanceNumber) + { + int pos = -1; + for (int i=0; i