/// <summary> /// Performs a lexicographical addition to a string by any amount. See /// http://wikipedia.org/wiki/Lexicographical_order, and further /// remarks for this member. /// </summary> /// <example>"code" + 2 in Ascii alpha (case insensitive) == "codg"</example> /// <example>"code" + 2 in Ascii alpha (case sensitive) == "codf"</example> /// <param name="source">The string to increment from.</param> /// <param name="charSet">The character set defining the characters and their order.</param> /// <param name="ignoreCase">Specifies if case should be ignored as an incremented value. /// If true, incremented character positions will be the case of the majority of other /// values; which may or may not be the same as the character being replaced.</param> /// <param name="count"></param> /// <returns></returns> /// <remarks> /// <p> /// This may or may not result in a value that sorts in the correct /// order as if it were a file name in a file explorer. /// </p> /// <p> /// If the string is at the highest character for each position or the /// number added moves past that position, then a new character position /// is incremented to the left (by adding a character position). This /// then behaves the same as if the source was left whitespace padded. /// Performance scales based on the number of characters incremented /// in the string. /// </p> /// <p> /// Incrementing the value always starts on the right and moves left /// as with numeric additions. Right whitespace padded strings will /// increment values in the whitespace before advancing to the characters /// on the left. If this is not desired behavior then Trim the source /// when passed into this method. /// </p> /// </remarks> public static String LexicalAdd(this String source, LexicalCharacterSet charSet, Boolean ignoreCase, Int32 count) { return LexicalAdd(source, charSet, ignoreCase, false, count); }
/// <summary> /// Increments a source string to the next logical higher value. Characters are incremented without altering length first. /// If all character positions are at the highest character, then a new lowest value character is added to the end. /// </summary> /// <param name="source">The source string.</param> /// <param name="charSet">The character set to use. If the source string characters do not all fit in this set, an exception is thrown.</param> /// <param name="ignoreCase">Specified if case is to be ignored for alpha characters.</param> /// <returns>The incremented string.</returns> /// <remarks> /// </remarks> public static String LexicalIncrement(this String source, LexicalCharacterSet charSet, Boolean ignoreCase) { return LexicalAdd(source, charSet, ignoreCase, 1); }
/// <summary> /// Performs a lexicographical addition to a string by any amount. See /// http://wikipedia.org/wiki/Lexicographical_order, and further /// remarks for this member. /// </summary> /// <example>"code" + 2 in Ascii alpha (case insensitive) == "codg"</example> /// <example>"code" + 2 in Ascii alpha (case sensitive) == "codf"</example> /// <param name="source">The string to increment from.</param> /// <param name="charSet">The character set defining the characters and their order.</param> /// <param name="ignoreCase">Specifies if case should be ignored as an incremented value. /// If true, incremented character positions will be the case of the majority of other /// values; which may or may not be the same as the character being replaced.</param> /// <param name="treatNonCharsAsSpace"> /// Indicates if non character set characters should be treated as /// a space and be eligible for incrementing. /// </param> /// <param name="count"></param> /// <returns></returns> /// <remarks> /// <p> /// This may or may not result in a value that sorts in the correct /// order as if it were a file name in a file explorer. /// </p> /// <p> /// If the string is at the highest character for each position or the /// number added moves past that position, then a new character position /// is incremented to the left (by adding a character position). This /// then behaves the same as if the source was left whitespace padded. /// Performance scales based on the number of characters incremented /// in the string. /// </p> /// <p> /// Incrementing the value always starts on the right and moves left /// as with numeric additions. Right whitespace padded strings will /// increment values in the whitespace before advancing to the characters /// on the left. If this is not desired behavior then Trim the source /// when passed into this method. /// </p> /// </remarks> public static String LexicalAdd(this String source, LexicalCharacterSet charSet, Boolean ignoreCase, Boolean treatNonCharsAsSpace, Int32 count) { var chars = source.ToCharArray().ToList(); //if (!chars.All(value => char.IsWhiteSpace(value) || charSet.Characters.Contains(value))) // throw new ArgumentException( // "The source string contains characters not available in the specified lexical increment character set."); if (count < 0) throw new ArgumentOutOfRangeException("count", count, "Only positive numbers can be added lexically at this time."); var characters = charSet.Characters; if (ignoreCase && charSet.IsCaseSensitive) { //change 'characters' to an upper or lower case version var lowerCount = chars.Where(char.IsLower).Count(); var upperCount = chars.Where(char.IsUpper).Count(); if (lowerCount > upperCount) characters = charSet.Characters.Where(value => !char.IsLetter(value) || char.IsLower(value)).ToList(); else characters = charSet.Characters.Where(value => !char.IsLetter(value) || char.IsUpper(value)).ToList(); } var mathBase = ignoreCase ? charSet.CaseInsensitiveLength : charSet.CaseSensitiveLength; Int32 remain = count, carryOver = 0; //position is counting from the right most character, since we are treating these characters as a number for (var position = 1; remain > 0 || carryOver > 0 ; position++) { if (chars.Count < position) { chars.Insert(0, ' '); } var positionBase = position == 1 ? 1 : Math.Pow(mathBase, position - 1); var posCount = ((int)((remain / positionBase) % mathBase)) + carryOver; if (posCount == mathBase) { //no character change, pass along carry over remain -= ((posCount - carryOver) * (int)positionBase); carryOver = 1; } else if (posCount > 0) { var posChar = chars[chars.Count - position]; var posCharIndex = characters.IndexOf(posChar); if (ignoreCase && posCharIndex == -1 && char.IsLetter(posChar)) { //Character removed when changing to an upper or lower case version, so get the equivalent case-insensitive character posChar = char.IsLower(posChar) ? char.ToUpper(posChar) : char.ToLower(posChar); posCharIndex = characters.IndexOf(posChar); } //if whitespace char, leave posCharIndex at -1 for replacement if (posCharIndex == -1 && !char.IsWhiteSpace(posChar) && !treatNonCharsAsSpace) throw new ArgumentException( "The source string contains characters not available in the specified lexical increment character set."); chars[chars.Count - position] = characters[((posCharIndex + posCount) % mathBase)]; carryOver = posCharIndex + posCount < characters.Count ? 0 : 1; remain = Math.Max(0, remain - posCount * (int)positionBase); } } return String.Concat(chars); }
/// <summary> /// Performs a lexicographical addition to a string by any amount. See /// http://wikipedia.org/wiki/Lexicographical_order, and further /// remarks for this member. /// </summary> /// <example>"code" + 2 in Ascii alpha (case insensitive) == "codg"</example> /// <example>"code" + 2 in Ascii alpha (case sensitive) == "codf"</example> /// <param name="source">The string to increment from.</param> /// <param name="charSet">The character set defining the characters and their order.</param> /// <param name="ignoreCase">Specifies if case should be ignored as an incremented value. /// If true, incremented character positions will be the case of the majority of other /// values; which may or may not be the same as the character being replaced.</param> /// <param name="count"></param> /// <returns></returns> /// <remarks> /// <p> /// This may or may not result in a value that sorts in the correct /// order as if it were a file name in a file explorer. /// </p> /// <p> /// If the string is at the highest character for each position or the /// number added moves past that position, then a new character position /// is incremented to the left (by adding a character position). This /// then behaves the same as if the source was left whitespace padded. /// Performance scales based on the number of characters incremented /// in the string. /// </p> /// <p> /// Incrementing the value always starts on the right and moves left /// as with numeric additions. Right whitespace padded strings will /// increment values in the whitespace before advancing to the characters /// on the left. If this is not desired behavior then Trim the source /// when passed into this method. /// </p> /// </remarks> public static String LexicalAdd(this String source, LexicalCharacterSet charSet, Boolean ignoreCase, Int32 count) { return(LexicalAdd(source, charSet, ignoreCase, false, count)); }
/// <summary> /// Increments a source string to the next logical higher value. Characters are incremented without altering length first. /// If all character positions are at the highest character, then a new lowest value character is added to the end. /// </summary> /// <param name="source">The source string.</param> /// <param name="charSet">The character set to use. If the source string characters do not all fit in this set, an exception is thrown.</param> /// <param name="ignoreCase">Specified if case is to be ignored for alpha characters.</param> /// <returns>The incremented string.</returns> /// <remarks> /// </remarks> public static String LexicalIncrement(this String source, LexicalCharacterSet charSet, Boolean ignoreCase) { return(LexicalAdd(source, charSet, ignoreCase, 1)); }
/// <summary> /// Performs a lexicographical addition to a string by any amount. See /// http://wikipedia.org/wiki/Lexicographical_order, and further /// remarks for this member. /// </summary> /// <example>"code" + 2 in Ascii alpha (case insensitive) == "codg"</example> /// <example>"code" + 2 in Ascii alpha (case sensitive) == "codf"</example> /// <param name="source">The string to increment from.</param> /// <param name="charSet">The character set defining the characters and their order.</param> /// <param name="ignoreCase">Specifies if case should be ignored as an incremented value. /// If true, incremented character positions will be the case of the majority of other /// values; which may or may not be the same as the character being replaced.</param> /// <param name="treatNonCharsAsSpace"> /// Indicates if non character set characters should be treated as /// a space and be eligible for incrementing. /// </param> /// <param name="count"></param> /// <returns></returns> /// <remarks> /// <p> /// This may or may not result in a value that sorts in the correct /// order as if it were a file name in a file explorer. /// </p> /// <p> /// If the string is at the highest character for each position or the /// number added moves past that position, then a new character position /// is incremented to the left (by adding a character position). This /// then behaves the same as if the source was left whitespace padded. /// Performance scales based on the number of characters incremented /// in the string. /// </p> /// <p> /// Incrementing the value always starts on the right and moves left /// as with numeric additions. Right whitespace padded strings will /// increment values in the whitespace before advancing to the characters /// on the left. If this is not desired behavior then Trim the source /// when passed into this method. /// </p> /// </remarks> public static String LexicalAdd(this String source, LexicalCharacterSet charSet, Boolean ignoreCase, Boolean treatNonCharsAsSpace, Int32 count) { var chars = source.ToCharArray().ToList(); //if (!chars.All(value => char.IsWhiteSpace(value) || charSet.Characters.Contains(value))) // throw new ArgumentException( // "The source string contains characters not available in the specified lexical increment character set."); if (count < 0) { throw new ArgumentOutOfRangeException("count", count, "Only positive numbers can be added lexically at this time."); } var characters = charSet.Characters; if (ignoreCase && charSet.IsCaseSensitive) { //change 'characters' to an upper or lower case version var lowerCount = chars.Where(char.IsLower).Count(); var upperCount = chars.Where(char.IsUpper).Count(); if (lowerCount > upperCount) { characters = charSet.Characters.Where(value => !char.IsLetter(value) || char.IsLower(value)).ToList(); } else { characters = charSet.Characters.Where(value => !char.IsLetter(value) || char.IsUpper(value)).ToList(); } } var mathBase = ignoreCase ? charSet.CaseInsensitiveLength : charSet.CaseSensitiveLength; Int32 remain = count, carryOver = 0; //position is counting from the right most character, since we are treating these characters as a number for (var position = 1; remain > 0 || carryOver > 0; position++) { if (chars.Count < position) { chars.Insert(0, ' '); } var positionBase = position == 1 ? 1 : Math.Pow(mathBase, position - 1); var posCount = ((int)((remain / positionBase) % mathBase)) + carryOver; if (posCount == mathBase) { //no character change, pass along carry over remain -= ((posCount - carryOver) * (int)positionBase); carryOver = 1; } else if (posCount > 0) { var posChar = chars[chars.Count - position]; var posCharIndex = characters.IndexOf(posChar); if (ignoreCase && posCharIndex == -1 && char.IsLetter(posChar)) { //Character removed when changing to an upper or lower case version, so get the equivalent case-insensitive character posChar = char.IsLower(posChar) ? char.ToUpper(posChar) : char.ToLower(posChar); posCharIndex = characters.IndexOf(posChar); } //if whitespace char, leave posCharIndex at -1 for replacement if (posCharIndex == -1 && !char.IsWhiteSpace(posChar) && !treatNonCharsAsSpace) { throw new ArgumentException( "The source string contains characters not available in the specified lexical increment character set."); } chars[chars.Count - position] = characters[((posCharIndex + posCount) % mathBase)]; carryOver = posCharIndex + posCount < characters.Count ? 0 : 1; remain = Math.Max(0, remain - posCount * (int)positionBase); } } return(String.Concat(chars)); }