c# pad left to string - c#

i want to find a efficent way to do :
i have a string like :
'1,2,5,11,33'
i want to pad zero only to the numbers that below 10 (have one digit)
so i want to get
'01,02,05,11,33'
thanks

How much do you really care about efficiency? Personally I'd use:
string padded = string.Join(",", original.Split(',')
.Select(x => x.PadLeft(2, '0')));
(As pointed out in the comments, if you're using .NET 3.5 you'll need a call to ToArray after the Select.)
That's definitely not the most efficient solution, but it's what I would use until I'd proved that it wasn't efficient enough. Here's an alternative...
// Make more general if you want, with parameters for the separator, length etc
public static string PadCommaSeparated(string text)
{
StringBuilder builder = new StringBuilder();
int start = 0;
int nextComma = text.IndexOf(',');
while (nextComma >= 0)
{
int itemLength = nextComma - start;
switch (itemLength)
{
case 0:
builder.Append("00,");
break;
case 1:
builder.Append("0");
goto default;
default:
builder.Append(text, start, itemLength);
builder.Append(",");
break;
}
start = nextComma + 1;
nextComma = text.IndexOf(',', start);
}
// Now deal with the end...
int finalItemLength = text.Length - start;
switch (finalItemLength)
{
case 0:
builder.Append("00");
break;
case 1:
builder.Append("0");
goto default;
default:
builder.Append(text, start, finalItemLength);
break;
}
return builder.ToString();
}
It's horrible code, but I think it will do what you want...

string input= "1,2,3,11,33";
string[] split = string.Split(input);
List<string> outputList = new List<string>();
foreach(var s in split)
{
outputList.Add(s.PadLeft(2, '0'));
}
string output = string.Join(outputList.ToArray(), ',');

Related

C# calculate multiple values from textbox

I am a student and got a task where I will have to make a program that solves first-grade equations. I will start of by making a textbox where I can write down different numbers of all the arithmetics, for example, 3+4*8 (it doesn't have to follow the priority rules) and then when I press the "go" button I get the answer.
I tried using the split method from this question/answer:
C# read and calculate multiple values from textbox and it worked for addition but then I tried to use the same script and change it up a little to make it work for multiplication and subtraction but it did not work.
The scripts that I have tried are:
string[] parts = tbxTal.Text.Split('+');
int intSumma = 0;
foreach (string item in parts)
{
intSumma = intSumma + Convert.ToInt32(item);
}
lblSvar.Text = intSumma.ToString();
Also tried using switch (+) after the split but it did not work since there is not + left after te splitt
Any Idea on how I can make a textbox that calculates everything inside of it? My teacher gave me a tip to use the split method and case method together.
Without giving the answer overtly, I would suggest that you keep a variable for the accumulator, operator, and operand. From there you can use a for loop to keep reading until you've evaluated all of the expression, then return the accumulator.
double Evaluate(string expression) {
double accumulator = 0;
double operand = 0;
string operator = string.Empty;
int index = 0;
while (index < expression.Length) {
operand = ExtractNextNumericValue(ref index, expression);
operator = ExtractNextOperator(ref index, expression);
// We now have everything we need to do the math
...
}
return accumulator;
}
public double ExtractNextNumericValue(ref index, string expression) {
// Use IndexOf on the string, use the index as a start location
// Make sure to update ref to be at the end of where you extracted your value
// You know that the value will come before an operator, so look for '+', '-', '*', '/'
...
}
One thing that should help you with creating a calculator like this is reversed Polish notation. Accepted answer to this question is a working calculator that can handle order of operations etc.
Code from mentioned post:
static void Main(string[] args)
{
String str = "5 + ( ( 1 + 2 ) * 4 ) −3";
String result=LengyelFormaKonvertalas(str);
Console.WriteLine(result.ToString());
Console.ReadLine();
}
static String LengyelFormaKonvertalas(String input) // this is the rpn method
{
Stack stack = new Stack();
String str = input.Replace(" ",string.Empty);
StringBuilder formula = new StringBuilder();
for (int i = 0; i < str.Length; i++)
{
char x=str[i];
if (x == '(')
stack.Push(x);
else if (IsOperandus(x)) // is it operand
{
formula.Append(x);
}
else if (IsOperator(x)) // is it operation
{
if (stack.Count>0 && (char)stack.Peek()!='(' && Prior(x)<=Prior((char)stack.Peek()) )
{
char y = (char)stack.Pop();
formula.Append(y);
}
if (stack.Count > 0 && (char)stack.Peek() != '(' && Prior(x) < Prior((char)stack.Peek()))
{
char y = (char)stack.Pop();
formula.Append(y);
}
stack.Push(x);
}
else
{
char y=(char)stack.Pop();
if (y!='(')
{
formula.Append(y);
}
}
}
while (stack.Count>0)
{
char c = (char)stack.Pop();
formula.Append(c);
}
return formula.ToString();
}
static bool IsOperator(char c)
{
return (c=='-'|| c=='+' || c=='*' || c=='/');
}
static bool IsOperandus(char c)
{
return (c>='0' && c<='9' || c=='.');
}
static int Prior(char c)
{
switch (c)
{
case '=':
return 1;
case '+':
return 2;
case '-':
return 2;
case '*':
return 3;
case '/':
return 3;
case '^':
return 4;
default:
throw new ArgumentException("Rossz paraméter");
}
}
}
Change below line of code from this article C# read and calculate multiple values from textbox
like this:
string[] parts = textBox1.Text.Split('+');
Replace above line with below line
string[] parts = textBox1.Text.Split('+','*','-');

Better regular expression for ReverseStringFormat

I've been using for a while this neat function found here on SO:
private List<string> ReverseStringFormat(string template, string str)
{
string pattern = "^" + Regex.Replace(template, #"\{[0-9]+\}", "(.*?)") + "$";
Regex r = new Regex(pattern);
Match m = r.Match(str);
List<string> ret = new List<string>();
for (int i = 1; i < m.Groups.Count; i++)
ret.Add(m.Groups[i].Value);
return ret;
}
This function is able to process correctly templates like:
My name is {0} and I'm {1} years old
While it fails with patterns like:
My name is {0} and I'm {1:00} years old
I would like to handle this failing scenario and add fixed length parsing.
The function transforms the (first) template as following:
My name is (.*?) and I'm (.*?) years old
I've been trying to write the above regular expression to limit the number of characters captured for the second group without success. This is my (terrible) attempt:
My name is (.*?) and I'm (.{2}) years old
I've been trying to process inputs like the following but the below PATTERN doesn't work:
PATTERN: My name is (.*?) (.{3})(.{5})
INPUT: My name is John 123ABCDE
EXPECTED OUTPUT: John, 123, ABCDE
Every suggestion is highly appreciated
It is highly unlikely that you will be able to measure the length of a captured group within the same Regex replacement.
I would strongly suggest you look at the following state machine implementation.
Please note that this implementation also solves the multiple curly brace escape feature of string.Format.
First you will need a state enum, very much like this one:
public enum State {
Outside,
OutsideAfterCurly,
Inside,
InsideAfterColon
}
Then you will need a nice way to iterate over each character in a string.
The string chars parameter represents your template parameter while the returning IEnumerable<string> represents consecutive parts of the resulting pattern:
public static IEnumerable<string> InnerTransmogrify(string chars) {
State state = State.Outside;
int counter = 0;
foreach (var #char in chars) {
switch (state) {
case State.Outside:
switch (#char) {
case '{':
state = State.OutsideAfterCurly;
break;
default:
yield return #char.ToString();
break;
}
break;
case State.OutsideAfterCurly:
switch (#char) {
case '{':
state = State.Outside;
break;
default:
state = State.Inside;
counter = 0;
yield return "(.";
break;
}
break;
case State.Inside:
switch (#char) {
case '}':
state = State.Outside;
yield return "*?)";
break;
case ':':
state = State.InsideAfterColon;
break;
default:
break;
}
break;
case State.InsideAfterColon:
switch (#char) {
case '}':
state = State.Outside;
yield return "{" + counter + "})";
break;
default:
counter++;
break;
}
break;
}
}
}
You could join the parts like so:
public static string Transmogrify(string chars) {
var parts = InnerTransmogrify(chars);
var result = string.Join("", parts);
return result;
}
And then wrap everything up, like you originally intended:
private List<string> ReverseStringFormat(string template, string str) {
string pattern = <<SOME_PLACE>> .Transmogrify(template);
Regex r = new Regex(pattern);
Match m = r.Match(str);
List<string> ret = new List<string>();
for (int i = 1; i < m.Groups.Count; i++)
ret.Add(m.Groups[i].Value);
return ret;
}
Hope you understand why the Regex language isn't expressive enough (at least as far as my understanding is concerned) for this sort of job.
The only way to solve your problem with regular expressions is using a custom matcher to replace the group capture length.
The code bellow does this in your example:
private static string PatternFromStringFormat(string template)
{
// replaces only elements like {0}
string firstPass = Regex.Replace(template, #"\{[0-9]+\}", "(.*?)");
// replaces elements like {0:000} using a custom matcher
string secondPass = Regex.Replace(firstPass, #"\{[0-9]+\:(?<len>[0-9]+)\}",
(match) =>
{
var len = match.Groups["len"].Value.Length;
return "(.{" + len + "*})";
});
return "^" + secondPass + "$";
}
private static List<string> ReverseStringFormat(string template, string str)
{
string pattern = PatternFromStringFormat(template);
Regex r = new Regex(pattern);
Match m = r.Match(str);
List<string> ret = new List<string>();
for (int i = 1; i < m.Groups.Count; i++)
ret.Add(m.Groups[i].Value);
return ret;
}

toTitleCase to ignore ordinals in C#

I'm trying to figure out a way to use toTitleCase to ignore ordinals. It works as I want it to for all string except for ordinals (e.g. 1st, 2nd, 3rd becomes 1St, 2Nd, 3Rd).
Any help would be appreciated. A regular expression may be the way to handle this, I'm just not sure how such a regex would be constructed.
Update: Here is the solution I used (Using John's answer I wrote below extension method):
public static string ToTitleCaseIgnoreOrdinals(this string text)
{
string input = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(text);
string result = System.Text.RegularExpressions.Regex.Replace(input, "([0-9]st)|([0-9]th)|([0-9]rd)|([0-9]nd)", new System.Text.RegularExpressions.MatchEvaluator((m) => m.Captures[0].Value.ToLower()), System.Text.RegularExpressions.RegexOptions.IgnoreCase);
return result;
}
string input = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase("hello there, this is the 1st");
string result = System.Text.RegularExpressions.Regex.Replace(input, "([0-9]st)|([0-9]th)|([0-9]rd)|([0-9]nd)", new System.Text.RegularExpressions.MatchEvaluator((m) =>
{
return m.Captures[0].Value.ToLower();
}), System.Text.RegularExpressions.RegexOptions.IgnoreCase);
You can use regular expressions to check if the string starts with a digit before you convert to Title Case, like this:
if (!Regex.IsMatch(text, #"^\d+"))
{
CultureInfo.CurrentCulture.TextInfo.toTitleCase(text);
}
Edit: forgot to reverse the conditional... changed so it will apply toTitleCase if it DOESN'T match.
2nd edit: added loop to check all words in a sentence:
string text = "150 east 40th street";
string[] array = text.Split(' ');
for (int i = 0; i < array.Length; i++)
{
if (!Regex.IsMatch(array[i], #"^\d+"))
{
array[i] = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(array[i]);
}
}
string newText = string.Join(" ",array);
I would split the text up and iterate through the resulting array, skipping things that don't start with a letter.
using System.Globalization;
TextInfo textInfo = new CultureInfo("en-US", false).TextInfo;
string[] text = myString.Split();
for(int i = 0; i < text.Length; i++)
{ //Check for zero-length strings, because these will throw an
//index out of range exception in Char.IsLetter
if (text[i].Length > 0 && Char.IsLetter(text[i][0]))
{
text[i] = textInfo.ToTitleCase(text[i]);
}
}
You could simply use String.Replace (or StringBuilder.Replace):
string[] ordinals = { "1St", "2Nd", "3Rd" }; // add all others
string text = "This is just sample text which contains some ordinals, the 1st, the 2nd and the third.";
var sb = new StringBuilder(CultureInfo.InvariantCulture.TextInfo.ToTitleCase(text));
foreach (string ordinal in ordinals)
sb.Replace(ordinal, ordinal.ToLowerInvariant());
text = sb.ToString();
this is not elegant at all. It requires you to maintain an infinite
list of ordinals on the first line. I'm assuming that's why someone
downvoted you.
It's not elegant but it works better than other simple approaches like the regex. You want to title-case words in longer text. But only words which are not ordinal-numbers. An ordinal number is f.e. 1st, 2nd or 3rd and 31st but not 31th. So the simple regex sollutions will fail fast. You also want to title-case words like 10m to 10M (where M could be the abbreviation for million).
So i don't understand why it's so bad to maintain a list of ordinal numbers.
You could even generate them automatically with an upper-limit, for example:
public static IEnumerable<string> GetTitleCaseOrdinalNumbers()
{
for (int num = 1; num <= int.MaxValue; num++)
{
switch (num % 100)
{
case 11:
case 12:
case 13:
yield return num + "Th";
break;
}
switch (num % 10)
{
case 1:
yield return num + "St"; break;
case 2:
yield return num + "Nd"; break;
case 3:
yield return num + "Rd"; break;
default:
yield return num + "Th"; break;
}
}
}
So if you want to check for the first 1000 ordinal numbers:
foreach (string ordinal in GetTitleCaseOrdinalNumbers().Take(1000))
sb.Replace(ordinal, ordinal.ToLowerInvariant());
Update
For what it's worth, here is my try to provide an efficient way that really checks words (and not only substrings) and skips ToTitleCase on words which really represent ordinal numbers(so not 31th but 31st for example). It also takes care of separator chars that are not white-spaces (like dots or commas):
private static readonly char[] separator = { '.', ',', ';', ':', '-', '(', ')', '\\', '{', '}', '[', ']', '/', '\\', '\'', '"', '"', '?', '!', '|' };
public static bool IsOrdinalNumber(string word)
{
if (word.Any(char.IsWhiteSpace))
return false; // white-spaces are not allowed
if (word.Length < 3)
return false;
var numericPart = word.TakeWhile(char.IsDigit);
string numberText = string.Join("", numericPart);
if (numberText.Length == 0)
return false;
int number;
if (!int.TryParse(numberText, out number))
return false; // handle unicode digits which are not really numeric like ۵
string ordinalNumber;
switch (number % 100)
{
case 11:
case 12:
case 13:
ordinalNumber = number + "th";
break;
}
switch (number % 10)
{
case 1:
ordinalNumber = number + "st"; break;
case 2:
ordinalNumber = number + "nd"; break;
case 3:
ordinalNumber = number + "rd"; break;
default:
ordinalNumber = number + "th"; break;
}
string checkForOrdinalNum = numberText + word.Substring(numberText.Length);
return checkForOrdinalNum.Equals(ordinalNumber, StringComparison.CurrentCultureIgnoreCase);
}
public static string ToTitleCaseIgnoreOrdinalNumbers(string text, TextInfo info)
{
if(text.Trim().Length < 3)
return info.ToTitleCase(text);
int whiteSpaceIndex = FindWhiteSpaceIndex(text, 0, separator);
if(whiteSpaceIndex == -1)
{
if(IsOrdinalNumber(text.Trim()))
return text;
else
return info.ToTitleCase(text);
}
StringBuilder sb = new StringBuilder();
int wordStartIndex = 0;
if(whiteSpaceIndex == 0)
{
// starts with space, find word
wordStartIndex = FindNonWhiteSpaceIndex(text, 1, separator);
sb.Append(text.Remove(wordStartIndex)); // append leading spaces
}
while(wordStartIndex >= 0)
{
whiteSpaceIndex = FindWhiteSpaceIndex(text, wordStartIndex + 1, separator);
string word;
if(whiteSpaceIndex == -1)
word = text.Substring(wordStartIndex);
else
word = text.Substring(wordStartIndex, whiteSpaceIndex - wordStartIndex);
if(IsOrdinalNumber(word))
sb.Append(word);
else
sb.Append(info.ToTitleCase(word));
wordStartIndex = FindNonWhiteSpaceIndex(text, whiteSpaceIndex + 1, separator);
string whiteSpaces;
if(wordStartIndex >= 0)
whiteSpaces = text.Substring(whiteSpaceIndex, wordStartIndex - whiteSpaceIndex);
else
whiteSpaces = text.Substring(whiteSpaceIndex);
sb.Append(whiteSpaces); // append spaces between words
}
return sb.ToString();
}
public static int FindWhiteSpaceIndex(string text, int startIndex = 0, params char[] separator)
{
bool checkSeparator = separator != null && separator.Any();
for (int i = startIndex; i < text.Length; i++)
{
char c = text[i];
if (char.IsWhiteSpace(c) || (checkSeparator && separator.Contains(c)))
return i;
}
return -1;
}
public static int FindNonWhiteSpaceIndex(string text, int startIndex = 0, params char[] separator)
{
bool checkSeparator = separator != null && separator.Any();
for (int i = startIndex; i < text.Length; i++)
{
char c = text[i];
if (!char.IsWhiteSpace(text[i]) && (!checkSeparator || !separator.Contains(c)))
return i;
}
return -1;
}
Note that this is really not tested yet but should give you an idea.
This would work for those strings, you could override ToTitleCase() via an Extension method.
string s = "1st";
if ( s[0] >= '0' && s[0] <= '9' ) {
//this string starts with a number
//so don't call ToTitleCase()
}
else { //call ToTileCase() }

Pars Text or script into C# code

I want to store combination of method invocations and formulas as text or script into database. for example, i want to store something like this in Database as a string and execute it somewhere in code:
if(Vessel.Weight>200)
{
return Cargo.Weight*Cargo.Tariff*2
}
else
{
Cargo.Weight*Cargo.Tariff
}
invocation:
var cost = executeFormula("CalculateTariff",new {Cargo= GetCargo(), Vessel=GetVessel()});
because these rules will change frequently and i don't want to deploy dll (CLR solution), and i don't want to store these rules as SP and mix business rules with DAL.
Any idea or tool?
If you place all values in a hashtable or a dictionary by changing the . with a _ (Vessel.Weight will become "Vessel_Weight") and simplify the syntax to one line it will be much easier to create a solution. This rule can be written for example as:
result=(Vessel_Weight>200)
?(Cargo_Weight*Cargo_Tariff*2)
:(Cargo_Weight*Cargo_Tariff)
Having rules defined like the above one you can use the following (draft, not optimal ...) code as a guide for a properly coded function that will do the job. I repeat that the following code is not perfect, but bottom line it's more than enough as a proof of concept.
Dictionary<string, dynamic> compute = new Dictionary<string, dynamic>();
compute.Add("Vessel_Weight", 123);
compute.Add("Cargo_Weight", 24);
compute.Add("Cargo_Tariff", 9);
string rule = "result=(Vessel_Weight>200)
?(Cargo_Weight*Cargo_Tariff*2)
:(Cargo_Weight*Cargo_Tariff)";
string process = rule.Replace(" ", "");
foreach (Match level1 in Regex.Matches(process, "\\([^\\)]+\\)"))
{
string parenthesis = level1.Value;
string keepit = parenthesis;
Console.Write("{0} -> ", parenthesis);
// replace all named variable with values from the dictionary
foreach (Match level2 in Regex.Matches(parenthesis, "[a-zA-z0-9_]+"))
{
string variable = level2.Value;
if (Regex.IsMatch(variable, "[a-zA-z_]+"))
{
if (!compute.ContainsKey(variable))
throw new Exception("Variable not found");
parenthesis = parenthesis.Replace(variable, compute[variable].ToString());
}
}
parenthesis = parenthesis.Replace("(", "").Replace(")", "");
Console.Write("{0} -> ", parenthesis);
// do the math
List<double> d = new List<double>();
foreach (Match level3 in Regex.Matches(parenthesis, "[0-9]+(\\.[0-9]+)?"))
{
d.Add(double.Parse(level3.Value));
parenthesis = Regex.Replace(parenthesis, level3.Value, "");
}
double start = d[0];
for (var i = 1; i < d.Count; i++)
{
switch (parenthesis[i - 1])
{
case '+':
start += d[i];
break;
case '-':
start -= d[i];
break;
case '*':
start *= d[i];
break;
case '/':
start /= d[i];
break;
case '=':
start = (start == d[i]) ? 0 : 1;
break;
case '>':
start = (start > d[i]) ? 0 : 1;
break;
case '<':
start = (start < d[i]) ? 0 : 1;
break;
}
}
parenthesis = start.ToString();
Console.WriteLine(parenthesis);
rule = rule.Replace(keepit, parenthesis);
}
Console.WriteLine(rule);
// peek a value in case of a condition
string condition = "[0-9]+(\\.[0-9]+)?\\?[0-9]+(\\.[0-9]+)?:[0-9]+(\\.[0-9]+)?";
if (Regex.IsMatch(rule, condition))
{
MatchCollection m = Regex.Matches(rule, "[0-9]+(\\.[0-9]+)?");
int check = int.Parse(m[0].Value) + 1;
rule = rule.Replace(Regex.Match(rule, condition).Value, m[check].Value);
}
Console.WriteLine(rule);
// final touch
int equal = rule.IndexOf("=");
compute.Add(rule.Substring(0, equal - 1), double.Parse(rule.Substring(equal + 1)));
Now the result is a named item in the dictionary. This way you may process more rules in the sense of intermediate results and have a final rule based on them. The code as is written does not guarantee correct execution order for arithmetic operations, but if you keep your rules simple (and possibly split them if is needed) your will achieve your goal.

Best way to convert Pascal Case to a sentence

What is the best way to convert from Pascal Case (upper Camel Case) to a sentence.
For example starting with
"AwaitingFeedback"
and converting that to
"Awaiting feedback"
C# preferable but I could convert it from Java or similar.
public static string ToSentenceCase(this string str)
{
return Regex.Replace(str, "[a-z][A-Z]", m => m.Value[0] + " " + char.ToLower(m.Value[1]));
}
In versions of visual studio after 2015, you can do
public static string ToSentenceCase(this string str)
{
return Regex.Replace(str, "[a-z][A-Z]", m => $"{m.Value[0]} {char.ToLower(m.Value[1])}");
}
Based on: Converting Pascal case to sentences using regular expression
I will prefer to use Humanizer for this. Humanizer is a Portable Class Library that meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities.
Short Answer
"AwaitingFeedback".Humanize() => Awaiting feedback
Long and Descriptive Answer
Humanizer can do a lot more work other examples are:
"PascalCaseInputStringIsTurnedIntoSentence".Humanize() => "Pascal case input string is turned into sentence"
"Underscored_input_string_is_turned_into_sentence".Humanize() => "Underscored input string is turned into sentence"
"Can_return_title_Case".Humanize(LetterCasing.Title) => "Can Return Title Case"
"CanReturnLowerCase".Humanize(LetterCasing.LowerCase) => "can return lower case"
Complete code is :
using Humanizer;
using static System.Console;
namespace HumanizerConsoleApp
{
class Program
{
static void Main(string[] args)
{
WriteLine("AwaitingFeedback".Humanize());
WriteLine("PascalCaseInputStringIsTurnedIntoSentence".Humanize());
WriteLine("Underscored_input_string_is_turned_into_sentence".Humanize());
WriteLine("Can_return_title_Case".Humanize(LetterCasing.Title));
WriteLine("CanReturnLowerCase".Humanize(LetterCasing.LowerCase));
}
}
}
Output
Awaiting feedback
Pascal case input string is turned into sentence
Underscored input string is turned into sentence Can Return Title Case
can return lower case
If you prefer to write your own C# code you can achieve this by writing some C# code stuff as answered by others already.
Here you go...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CamelCaseToString
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(CamelCaseToString("ThisIsYourMasterCallingYou"));
}
private static string CamelCaseToString(string str)
{
if (str == null || str.Length == 0)
return null;
StringBuilder retVal = new StringBuilder(32);
retVal.Append(char.ToUpper(str[0]));
for (int i = 1; i < str.Length; i++ )
{
if (char.IsLower(str[i]))
{
retVal.Append(str[i]);
}
else
{
retVal.Append(" ");
retVal.Append(char.ToLower(str[i]));
}
}
return retVal.ToString();
}
}
}
This works for me:
Regex.Replace(strIn, "([A-Z]{1,2}|[0-9]+)", " $1").TrimStart()
This is just like #SSTA, but is more efficient than calling TrimStart.
Regex.Replace("ThisIsMyCapsDelimitedString", "(\\B[A-Z])", " $1")
Found this in the MvcContrib source, doesn't seem to be mentioned here yet.
return Regex.Replace(input, "([A-Z])", " $1", RegexOptions.Compiled).Trim();
Just because everyone has been using Regex (except this guy), here's an implementation with StringBuilder that was about 5x faster in my tests. Includes checking for numbers too.
"SomeBunchOfCamelCase2".FromCamelCaseToSentence == "Some Bunch Of Camel Case 2"
public static string FromCamelCaseToSentence(this string input) {
if(string.IsNullOrEmpty(input)) return input;
var sb = new StringBuilder();
// start with the first character -- consistent camelcase and pascal case
sb.Append(char.ToUpper(input[0]));
// march through the rest of it
for(var i = 1; i < input.Length; i++) {
// any time we hit an uppercase OR number, it's a new word
if(char.IsUpper(input[i]) || char.IsDigit(input[i])) sb.Append(' ');
// add regularly
sb.Append(input[i]);
}
return sb.ToString();
}
Here's a basic way of doing it that I came up with using Regex
public static string CamelCaseToSentence(this string value)
{
var sb = new StringBuilder();
var firstWord = true;
foreach (var match in Regex.Matches(value, "([A-Z][a-z]+)|[0-9]+"))
{
if (firstWord)
{
sb.Append(match.ToString());
firstWord = false;
}
else
{
sb.Append(" ");
sb.Append(match.ToString().ToLower());
}
}
return sb.ToString();
}
It will also split off numbers which I didn't specify but would be useful.
string camel = "MyCamelCaseString";
string s = Regex.Replace(camel, "([A-Z])", " $1").ToLower().Trim();
Console.WriteLine(s.Substring(0,1).ToUpper() + s.Substring(1));
Edit: didn't notice your casing requirements, modifed accordingly. You could use a matchevaluator to do the casing, but I think a substring is easier. You could also wrap it in a 2nd regex replace where you change the first character
"^\w"
to upper
\U (i think)
I'd use a regex, inserting a space before each upper case character, then lowering all the string.
string spacedString = System.Text.RegularExpressions.Regex.Replace(yourString, "\B([A-Z])", " \k");
spacedString = spacedString.ToLower();
It is easy to do in JavaScript (or PHP, etc.) where you can define a function in the replace call:
var camel = "AwaitingFeedbackDearMaster";
var sentence = camel.replace(/([A-Z].)/g, function (c) { return ' ' + c.toLowerCase(); });
alert(sentence);
Although I haven't solved the initial cap problem... :-)
Now, for the Java solution:
String ToSentence(String camel)
{
if (camel == null) return ""; // Or null...
String[] words = camel.split("(?=[A-Z])");
if (words == null) return "";
if (words.length == 1) return words[0];
StringBuilder sentence = new StringBuilder(camel.length());
if (words[0].length() > 0) // Just in case of camelCase instead of CamelCase
{
sentence.append(words[0] + " " + words[1].toLowerCase());
}
else
{
sentence.append(words[1]);
}
for (int i = 2; i < words.length; i++)
{
sentence.append(" " + words[i].toLowerCase());
}
return sentence.toString();
}
System.out.println(ToSentence("AwaitingAFeedbackDearMaster"));
System.out.println(ToSentence(null));
System.out.println(ToSentence(""));
System.out.println(ToSentence("A"));
System.out.println(ToSentence("Aaagh!"));
System.out.println(ToSentence("stackoverflow"));
System.out.println(ToSentence("disableGPS"));
System.out.println(ToSentence("Ahh89Boo"));
System.out.println(ToSentence("ABC"));
Note the trick to split the sentence without loosing any character...
Pseudo-code:
NewString = "";
Loop through every char of the string (skip the first one)
If char is upper-case ('A'-'Z')
NewString = NewString + ' ' + lowercase(char)
Else
NewString = NewString + char
Better ways can perhaps be done by using regex or by string replacement routines (replace 'X' with ' x')
An xquery solution that works for both UpperCamel and lowerCamel case:
To output sentence case (only the first character of the first word is capitalized):
declare function content:sentenceCase($string)
{
let $firstCharacter := substring($string, 1, 1)
let $remainingCharacters := substring-after($string, $firstCharacter)
return
concat(upper-case($firstCharacter),lower-case(replace($remainingCharacters, '([A-Z])', ' $1')))
};
To output title case (first character of each word capitalized):
declare function content:titleCase($string)
{
let $firstCharacter := substring($string, 1, 1)
let $remainingCharacters := substring-after($string, $firstCharacter)
return
concat(upper-case($firstCharacter),replace($remainingCharacters, '([A-Z])', ' $1'))
};
Found myself doing something similar, and I appreciate having a point-of-departure with this discussion. This is my solution, placed as an extension method to the string class in the context of a console application.
using System;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string piratese = "avastTharMatey";
string ivyese = "CheerioPipPip";
Console.WriteLine("{0}\n{1}\n", piratese.CamelCaseToString(), ivyese.CamelCaseToString());
Console.WriteLine("For Pete\'s sake, man, hit ENTER!");
string strExit = Console.ReadLine();
}
}
public static class StringExtension
{
public static string CamelCaseToString(this string str)
{
StringBuilder retVal = new StringBuilder(32);
if (!string.IsNullOrEmpty(str))
{
string strTrimmed = str.Trim();
if (!string.IsNullOrEmpty(strTrimmed))
{
retVal.Append(char.ToUpper(strTrimmed[0]));
if (strTrimmed.Length > 1)
{
for (int i = 1; i < strTrimmed.Length; i++)
{
if (char.IsUpper(strTrimmed[i])) retVal.Append(" ");
retVal.Append(char.ToLower(strTrimmed[i]));
}
}
}
}
return retVal.ToString();
}
}
}
Most of the preceding answers split acronyms and numbers, adding a space in front of each character. I wanted acronyms and numbers to be kept together so I have a simple state machine that emits a space every time the input transitions from one state to the other.
/// <summary>
/// Add a space before any capitalized letter (but not for a run of capitals or numbers)
/// </summary>
internal static string FromCamelCaseToSentence(string input)
{
if (string.IsNullOrEmpty(input)) return String.Empty;
var sb = new StringBuilder();
bool upper = true;
for (var i = 0; i < input.Length; i++)
{
bool isUpperOrDigit = char.IsUpper(input[i]) || char.IsDigit(input[i]);
// any time we transition to upper or digits, it's a new word
if (!upper && isUpperOrDigit)
{
sb.Append(' ');
}
sb.Append(input[i]);
upper = isUpperOrDigit;
}
return sb.ToString();
}
And here's some tests:
[TestCase(null, ExpectedResult = "")]
[TestCase("", ExpectedResult = "")]
[TestCase("ABC", ExpectedResult = "ABC")]
[TestCase("abc", ExpectedResult = "abc")]
[TestCase("camelCase", ExpectedResult = "camel Case")]
[TestCase("PascalCase", ExpectedResult = "Pascal Case")]
[TestCase("Pascal123", ExpectedResult = "Pascal 123")]
[TestCase("CustomerID", ExpectedResult = "Customer ID")]
[TestCase("CustomABC123", ExpectedResult = "Custom ABC123")]
public string CanSplitCamelCase(string input)
{
return FromCamelCaseToSentence(input);
}
Mostly already answered here
Small chage to the accepted answer, to convert the second and subsequent Capitalised letters to lower case, so change
if (char.IsUpper(text[i]))
newText.Append(' ');
newText.Append(text[i]);
to
if (char.IsUpper(text[i]))
{
newText.Append(' ');
newText.Append(char.ToLower(text[i]));
}
else
newText.Append(text[i]);
Here is my implementation. This is the fastest that I got while avoiding creating spaces for abbreviations.
public static string PascalCaseToSentence(string input)
{
if (string.IsNullOrEmpty(input) || input.Length < 2)
return input;
var sb = new char[input.Length + ((input.Length + 1) / 2)];
var len = 0;
var lastIsLower = false;
for (int i = 0; i < input.Length; i++)
{
var current = input[i];
if (current < 97)
{
if (lastIsLower)
{
sb[len] = ' ';
len++;
}
lastIsLower = false;
}
else
{
lastIsLower = true;
}
sb[len] = current;
len++;
}
return new string(sb, 0, len);
}

Categories