HttpStatusCode to readable string - c#

HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync();
HttpStatusCode statusCode = response.StatusCode;
In this code statusCode.ToString() returns for example "BadRequest" but I need "Bad Request"
I saw arcticles about response.ReasonPhrase, but that's not what I need and it is not supported by HttpWebResponse, only supported by HttpResponseMessage from HttpClient
Another example against Regex.Replace solution:
(414) RequestUriTooLong -> Request-Uri Too Long

Based on reference source, you can retrieve the English status description with a simple call into a static class, given the status code:
int code = 400;
/* will assign "Bad Request" to text */
var text = System.Web.HttpWorkerRequest.GetStatusDescription(code);
Texts are defined for ranges 100 - 507, returning empty strings for special codes like 418 and 506.

HttpStatusCode is an enum which has camel-cased member names.
You can use this one-liner to accomplish your need by putting space between camel-cased:
return Regex.Replace(statusCode.ToString(), "(?<=[a-z])([A-Z])", " $1", RegexOptions.Compiled);

Here, I pulled this out of a string utility class I have. Might be overkill, but it's a useful thing. Use the ToTitleCase extension method.
/// <summary>
/// A dictionary that holds the collection of previous title case
/// conversions so they don't have to be done again if needed more than once.
/// </summary>
private static Dictionary<string, string> _prevTitleCaseConversions = new Dictionary<String, String>();
/// <summary>
/// A collection of English words that should be lower-case in title-cased phrases.
/// </summary>
private static List<string> _englishTitleCaseLowerCaseWords = new List<string>() {"aboard", "about", "above", "across", "after",
"against", "along", "amid", "among", "anti", "around", "is", "as", "at", "before", "behind", "below",
"beneath", "beside", "besides", "between", "beyond", "but", "by", "concerning", "considering",
"despite", "down", "during", "except", "excepting", "excluding", "following", "for", "from", "in",
"inside", "into", "like", "minus", "near", "of", "off", "on", "onto", "opposite", "outside", "over",
"past", "per", "plus", "regarding", "round", "save", "since", "than", "through", "to", "toward",
"towards", "under", "underneath", "unlike", "until", "up", "upon", "versus", "via", "with", "within", "without",
"and", "but", "or", "nor", "for", "yet", "so", "although", "because", "since", "unless", "the", "a", "an"};
/// <summary>
/// Convert the provided alpha-numeric string to title case. The string may contain spaces in addition to letters and numbers, or it can be
/// one individual lowercase, uppercase, or camel case token.
/// </summary>
/// <param name="forValue">The input string which will be converted. The string can be a
/// normal string with spaces or a single token in all lowercase, all uppercase, or camel case.</param>
/// <returns>A version of the input string which has had spaces inserted between each internal "word" that is
/// delimited by an uppercase character and which has otherwise been converted to title case, i.e. all
/// words except for conjunctions, prepositions, and articles are upper case.</returns>
public static string ToTitleCase(this string forValue)
{
if (string.IsNullOrEmpty(forValue)) return forValue;
if (!Regex.IsMatch(forValue, "^[A-Za-z0-9 ]+$"))
throw new ArgumentException($#"""{forValue}"" is not a valid alpha-numeric token for this method.");
if (_prevTitleCaseConversions.ContainsKey(forValue)) return _prevTitleCaseConversions[forValue];
var tokenizedChars = GetTokenizedCharacterArray(forValue);
StringBuilder wordsSB = GetTitleCasedTokens(tokenizedChars);
string ret = wordsSB.ToString();
_prevTitleCaseConversions.Add(forValue, ret);
return ret;
}
/// <summary>
/// Convert the provided string such that first character is
/// uppercase and the remaining characters are lowercase.
/// </summary>
/// <param name="forInput">The string which will have
/// its first character converted to uppercase and
/// subsequent characters converted to lowercase.</param>
/// <returns>The provided string with its first character
/// converted to uppercase and subsequent characters converted to lowercase.</returns>
private static string FirstUpper(this string forInput)
{
return Alter(forInput, new Func<string, string>((input) => input.ToUpperInvariant()), new Func<string, string>((input) => input.ToLowerInvariant()));
}
/// <summary>
/// Return an array of characters built from the provided string with
/// spaces in between each word (token).
/// </summary>
private static ReadOnlyCollection<char> GetTokenizedCharacterArray(string fromInput)
{
var ret = new List<char>();
var tokenChars = fromInput.ToCharArray();
bool isPrevCharUpper = false;
bool isPrevPrevCharUpper = false;
bool isPrevPrevPrevCharUpper = false;
bool isNextCharUpper = false;
bool isNextCharSpace = false;
for (int i = 0; i < tokenChars.Length; i++)
{
char letter = tokenChars[i];
bool addSpace;
bool isCharUpper = char.IsUpper(letter);
if (i == 0) addSpace = false; // no space before first char.
else
{
bool isAtLastChar = i == tokenChars.Length - 1;
isNextCharUpper = !isAtLastChar && char.IsUpper(tokenChars[i + 1]);
isNextCharSpace = !isAtLastChar && !isNextCharUpper && tokenChars[i + 1].Equals(' ');
bool isInAcronym = (isCharUpper && isPrevCharUpper && (isAtLastChar || isNextCharSpace || isNextCharUpper));
addSpace = isCharUpper && !isInAcronym;
}
if (addSpace) ret.Add(' ');
ret.Add(letter);
isPrevPrevPrevCharUpper = isPrevPrevCharUpper;
isPrevPrevCharUpper = isPrevCharUpper;
isPrevCharUpper = isCharUpper;
}
return ret.AsReadOnly();
}
/// <summary>
/// Return a string builder that will produce a string which contains
/// all the tokens (words separated by spaces) in the provided collection
/// of characters and where the string conforms to title casing rules as defined above.
/// </summary>
private static StringBuilder GetTitleCasedTokens(IEnumerable<char> fromTokenChars)
{
StringBuilder wordsSB = new StringBuilder();
var comparer = StringComparer.Create(System.Globalization.CultureInfo.CurrentCulture, true);
var words = new string(fromTokenChars.ToArray()).Split(' ');
bool isFirstWord = true;
foreach (string word in words)
{
if (word.Length == 0) continue;
if (wordsSB.Length > 0) wordsSB.Append(' ');
bool isAcronym = word.Length > 1 && word.All((c) => char.IsUpper(c));
string toAppend;
// leave acronyms as-is, and lower-case all title case exceptions unless it's the first word.
if (isAcronym) toAppend = word;
else if (isFirstWord || !_englishTitleCaseLowerCaseWords.Contains(word, comparer)) toAppend = word.FirstUpper();
else toAppend = word.ToLower();
wordsSB.Append(toAppend);
isFirstWord = false;
}
return wordsSB;
}
/// <summary>
/// Convert the provided string such that first character is altered using
/// <paramref name="firstCharAlterationFunction"/> and the remaining characters
/// are altered using <paramref name="remainingCharsAlterationFunction"/>.
/// </summary>
/// <param name="forInput">The string which will have
/// its first character altered using <paramref name="firstCharAlterationFunction"/> and
/// subsequent characters altered using <paramref name="remainingCharsAlterationFunction"/>.</param>
/// <param name="firstCharAlterationFunction">The function which will
/// be used to alter the first character of the input string.</param>
/// <param name="remainingCharsAlterationFunction">The function which
/// will be used to ever character in the string after the first character.</param>
/// <returns>The provided string with its first character
/// altered using <paramref name="firstCharAlterationFunction"/> and
/// subsequent characters altered using <paramref name="remainingCharsAlterationFunction"/>.</returns>
private static string Alter(string forInput, Func<string, string> firstCharAlterationFunction, Func<string, string> remainingCharsAlterationFunction)
{
if (string.IsNullOrWhiteSpace(forInput)) return forInput;
if (forInput.Length == 1) return firstCharAlterationFunction(forInput);
return firstCharAlterationFunction(forInput[0].ToString()) + remainingCharsAlterationFunction(forInput.Substring(1));
}

Related

Remove html tags from MainBody

Have an issue here where I try to remove all html tags from this line of EPiServer code
#(Html.PropertyFor(m => m.MainBody)
Because this is suppose to be inside a <a>example code here</a>
Whats a good way to solve this when running EPi Server?
First, it is bad practice using XhtmlString this way, that being said we don't always get to choose.
I'm using this which is a modified version of Rob Volk's extension method.
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
public static class HtmlStringExtensions
{
/// <summary>
/// Truncates a string containing HTML to a number of text characters, keeping whole words.
/// The result contains HTML and any tags left open are closed.
/// by Rob Volk with modifications
/// http://robvolk.com/truncate-html-string-c-extension-method/
/// </summary>
/// <param name="html"></param>
/// <param name="maxCharacters"></param>
/// <param name="trailingText"></param>
/// <returns></returns>
public static string TruncateHtmlString(this string html, int maxCharacters, string trailingText)
{
if (string.IsNullOrEmpty(html))
return html;
// find the spot to truncate
// count the text characters and ignore tags
var textCount = 0;
var charCount = 0;
var ignore = false;
var newString = string.Empty;
foreach (char c in html)
{
newString += c;
charCount++;
if (c == '<')
{
ignore = true;
}
else if (!ignore)
{
textCount++;
}
if (c == '>')
{
ignore = false;
}
// stop once we hit the limit
if (textCount >= maxCharacters)
{
break;
}
}
// Truncate the html and keep whole words only
var trunc = new StringBuilder(newString);
//var trunc = new StringBuilder(html.TruncateWords(charCount));
// keep track of open tags and close any tags left open
var tags = new Stack<string>();
var matches = Regex.Matches(trunc.ToString(), // trunc.ToString()
#"<((?<tag>[^\s/>]+)|/(?<closeTag>[^\s>]+)).*?(?<selfClose>/)?\s*>",
RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Multiline);
foreach (Match match in matches)
{
if (match.Success)
{
var tag = match.Groups["tag"].Value;
var closeTag = match.Groups["closeTag"].Value;
// push to stack if open tag and ignore it if it is self-closing, i.e. <br />
if (!string.IsNullOrEmpty(tag) && string.IsNullOrEmpty(match.Groups["selfClose"].Value))
tags.Push(tag);
// pop from stack if close tag
else if (!string.IsNullOrEmpty(closeTag))
{
// pop the tag to close it.. find the matching opening tag
// ignore any unclosed tags
while (tags.Pop() != closeTag && tags.Count > 0)
{ }
}
}
}
if (html.Length > charCount)
// add the trailing text
trunc.Append(trailingText);
// pop the rest off the stack to close remainder of tags
while (tags.Count > 0)
{
trunc.Append("</");
trunc.Append(tags.Pop());
trunc.Append('>');
}
return trunc.ToString();
}
/// <summary>
/// Truncates a string containing HTML to a number of text characters, keeping whole words.
/// The result contains HTML and any tags left open are closed.
/// </summary>
/// <param name="html"></param>
/// <param name="maxCharacters"></param>
/// <returns></returns>
public static string TruncateHtmlString(this string html, int maxCharacters)
{
return html.TruncateHtmlString(maxCharacters, null);
}
/// <summary>
/// Strips all HTML tags from a string
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static string StripHtml(this string html)
{
if (string.IsNullOrEmpty(html))
return html;
return Regex.Replace(html, #"<(.|\n)*?>", string.Empty);
}
}
Implement using the ToHtmlString() from EPiServer.Core
In example
// #using EPiServer.Core
#(Html.PropertyFor(m => m.MainBody.ToHtmlString().TruncateHtmlString(160, "..."))
Why don't you use string backed by TextArea?
[UIHint(UIHint.Textarea)]
[Display(Name = "Main Body")]
public virtual string MainBody { get; set; }
What you trying to do using XhtmlString is not a best practise and it could have so many negative effects on your rendering.

What is c# alternative of splice in c++

I have a c++ code and trying to write in C#
I couldn't figure out best alternative to splice in c#
Also C++ has a 'find' too to work on map,which I want to implement in C# on dictionary
In your C++ example, you show:
statement_tokens.splice(statement_tokens.begin(), tokens, tokens.begin(), next_sc);
From what I understand (documentation), this overload takes an insert position, a list (of the same type), and the first (inclusive) and last (exclusive) indexes of a range to splice into the insert position, and then inserts this range into the original list.
Update: AND it removes the items from the other list. I just added that functionality.
If this is correct, then the following extension method should work:
List Extension Method (check the end of this answer for other overloads of this method)
public static class ListExtensions
{
public static void Splice<T>(this List<T> list, int insertAtIndex, List<T> items,
int first, int last)
{
if (items == null) return;
insertAtIndex = Math.Min(list.Count, Math.Max(0, insertAtIndex));
first = Math.Min(items.Count - 1, Math.Max(0, first));
last = Math.Min(items.Count, Math.Max(1, last));
if (first >= last) return;
list.InsertRange(insertAtIndex, items.GetRange(first, last - first));
items.RemoveRange(first, last - first);
}
}
Update 2: Now, it looks like you're missing another extension method for std::find_if, which returns the index of a list item in a specified range, based on a method that returns true if the item meets some condition. So let's add the following method to the ListExtensions class above:
public static int FindIf<T>(this List<T> list, int start, int end, Func<T, bool> method)
{
if (method == null) return end;
if (!list.Any(method)) return end;
start = Math.Min(list.Count - 1, Math.Max(0, start));
end = Math.Min(list.Count, Math.Max(1, end));
if (start >= end) return end;
var range = list.GetRange(start, end - start);
var index = range.IndexOf(list.First(method));
if (index < start) return end;
return index;
}
Notice that one of the arguments to this method is a function that takes an item of type T and returns a bool. This will be a simple method that checks if the string value of our token is a semicolon:
static bool TokenIsSemicolon(EvlToken token)
{
return (token != null && token.Str == ";");
}
Now, you may notice that I referenced token.Str. This is from the EvlToken class, which was created to mimic the C++ struct:
class EvlToken
{
public enum TokenType { Name, Number, Single }
public TokenType Type { get; set; }
public string Str { get; set; }
public int LineNo { get; set; }
}
Now we can finish the conversion of the original method, calling our FindIf and Splice extension methods:
static bool MoveTokensToStatement(List<EvlToken> statementTokens, List<EvlToken> tokens)
{
if (statementTokens == null || statementTokens.Count > 0) return false;
if (tokens == null || tokens.Count == 0) return false;
int nextSemiColon = tokens.FindIf(0, tokens.Count, TokenIsSemicolon);
if (nextSemiColon == tokens.Count)
{
Console.WriteLine("Looked for ';' but reached the end of the file.");
return false;
}
++nextSemiColon;
statementTokens.Splice(0, tokens, 0, nextSemiColon);
return true;
}
Additional Overloads
For completeness, here is the extensions class with the other two overloads mentioned in the documentation:
public static class ListExtensions
{
/// <summary>
/// Transfers all elements from 'items' into 'this' at the specified index
/// </summary>
/// <typeparam name="T">The type of items in the list</typeparam>
/// <param name="list">'this' instance</param>
/// <param name="insertAtIndex">The index to insert the items</param>
/// <param name="items">The list to transfer the items from</param>
public static void Splice<T>(this List<T> list, int insertAtIndex,
List<T> items)
{
if (items == null) return;
list.Splice(insertAtIndex, items, 0, items.Count);
}
/// <summary>
/// Transfers the element at 'itemIndex' from 'items'
/// into 'this' at the specified index
/// </summary>
/// <typeparam name="T">The type of items in the list</typeparam>
/// <param name="list">'this' instance</param>
/// <param name="insertAtIndex">The index to insert the item</param>
/// <param name="items">The list to transfer the item from</param>
/// <param name="itemIndex">The index of the item to transfer</param>
public static void Splice<T>(this List<T> list, int insertAtIndex,
List<T> items, int itemIndex)
{
list.Splice(insertAtIndex, items, itemIndex, itemIndex + 1);
}
/// <summary>
/// Transfers the specified range of elements from 'items'
/// into 'this' at the specified index
/// </summary>
/// <typeparam name="T">The type of items in the list</typeparam>
/// <param name="list">'this' instance</param>
/// <param name="insertAtIndex">The index to insert the item</param>
/// <param name="items">The list to transfer the item from</param>
/// <param name="first">The index of the first item in the range</param>
/// <param name="last">The exclusive index of the last item in the range</param>
public static void Splice<T>(this List<T> list, int insertAtIndex, List<T> items,
int first, int last)
{
if (items == null) return;
insertAtIndex = Math.Min(list.Count, Math.Max(0, insertAtIndex));
first = Math.Min(items.Count - 1, Math.Max(0, first));
last = Math.Min(items.Count, Math.Max(1, last));
if (first >= last) return;
list.InsertRange(insertAtIndex, items.GetRange(first, last - first));
items.RemoveRange(first, last - first);
}
/// <summary>
/// Searches for the first item in the specified range that "method" returns true for
/// </summary>
/// <typeparam name="T">The type of items in the list</typeparam>
/// <param name="list">'this' instance</param>
/// <param name="start">The index of the first item in the range</param>
/// <param name="end">The exclusive index of the last item in the range</param>
/// <param name="method">A method which takes type 'T' and returns a bool</param>
/// <returns>The index of the item, if found, otherwise 'end'</returns>
public static int FindIf<T>(this List<T> list, int start, int end, Func<T, bool> method)
{
if (method == null) return end;
if (!list.Any(method)) return end;
start = Math.Min(list.Count - 1, Math.Max(0, start));
end = Math.Min(list.Count, Math.Max(1, end));
if (start >= end) return end;
var range = list.GetRange(start, end - start);
var index = range.IndexOf(list.First(method));
if (index < start) return end;
return index;
}
}
Example Usage
Here's an example using a list of EvlTokens, and then calling MoveTokensToStatement twice:
private static void Main()
{
var tokens = new List<EvlToken>
{
new EvlToken {LineNo = 3, Str = "int", Type = EvlToken.TokenType.Single},
new EvlToken {LineNo = 3, Str = "x", Type = EvlToken.TokenType.Name},
new EvlToken {LineNo = 3, Str = "=", Type = EvlToken.TokenType.Single},
new EvlToken {LineNo = 3, Str = "1", Type = EvlToken.TokenType.Number},
new EvlToken {LineNo = 3, Str = "+", Type = EvlToken.TokenType.Single},
new EvlToken {LineNo = 3, Str = "5", Type = EvlToken.TokenType.Number},
new EvlToken {LineNo = 3, Str = ";", Type = EvlToken.TokenType.Single},
new EvlToken {LineNo = 4, Str = "Console", Type = EvlToken.TokenType.Single},
new EvlToken {LineNo = 4, Str = ".", Type = EvlToken.TokenType.Single},
new EvlToken {LineNo = 4, Str = "WriteLine", Type = EvlToken.TokenType.Single},
new EvlToken {LineNo = 4, Str = "(", Type = EvlToken.TokenType.Single},
new EvlToken {LineNo = 4, Str = "Hello World", Type = EvlToken.TokenType.Single},
new EvlToken {LineNo = 4, Str = ")", Type = EvlToken.TokenType.Single},
new EvlToken {LineNo = 4, Str = ";", Type = EvlToken.TokenType.Single}
};
var statementTokens = new List<EvlToken>();
MoveTokensToStatement(statementTokens, tokens);
Console.WriteLine("Here is the result of calling 'MoveTokensToStatement' the first time:");
Console.WriteLine(string.Join(" ", statementTokens.Select(t => t.Str)));
statementTokens.Clear();
MoveTokensToStatement(statementTokens, tokens);
Console.WriteLine("\nHere is the result of calling 'MoveTokensToStatement' the second time:");
Console.WriteLine(string.Join("", statementTokens.Select(t => t.Str)));
statementTokens.Clear();
Console.WriteLine("\nDone!\nPress any key to exit...");
Console.ReadKey();
}
Output
#RufusL shows you how to write a method with the same postcondition, but doesn't really discuss any other characteristics of the algorithm. In particular, his algorithm has higher complexity than the C++ splice.
Namely, splice on a doubly linked list such as C++'s std::list is an O(1) operation, because it only requires a constant number of pointer swaps.
.NET does have a doubly linked list class in the base library, which is System.Collections.Generic.LinkedList, but it keeps pointers back to the list from each node (System.Collections.Generic.LinkedListNode, List property) and each list stores a count. As a result, in addition to the constant number of swaps of the forward and back pointers, O(n) node-to-list pointer updates will be required, and O(n) "distance" calculation is required in order to update the Count field on both lists.
So in order to achieve a true equivalent (O(1)) to C++ std::list::splice, one has to abandon the BCL LinkedList class and make a custom doubly linked list, with neither a cached Count field (LINQ Count() that walks the list will still work) nor pointers from list nodes to the list.

Regex camelcase in c#

I'm trying to use regex to convert a string like this "North Korea"
to a string like "northKorea" - does someone know how I might accomplish this in c# ?
Cheers
if you know all your input strings are in title case (like "North Korea") you can simply do:
string input = "North Korea";
input = input.Replace(" ",""); //remove spaces
string output = char.ToLower(input[0]) +
input.Substring(1); //make first char lowercase
// output = "northKorea"
if some of your input is not in title case you can use TextInfo.ToTitleCase
string input = "NoRtH kORea";
input = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(input);
input = input.Replace(" ",""); //remove spaces
string output = char.ToLower(input[0]) +
input.Substring(1); //make first char lowercase
// output = "northKorea"
Forget regex.
All you need is a camelCase conversion algorithm:
See here:
http://www.codekeep.net/snippets/096fea45-b426-40fd-8beb-dec49d8a8662.aspx
Use this one:
string camelCase = ConvertCaseString(a, Case.CamelCase);
Copy-pasted in case it goes offline:
void Main() {
string a = "background color-red.brown";
string camelCase = ConvertCaseString(a, Case.CamelCase);
string pascalCase = ConvertCaseString(a, Case.PascalCase);
}
/// <summary>
/// Converts the phrase to specified convention.
/// </summary>
/// <param name="phrase"></param>
/// <param name="cases">The cases.</param>
/// <returns>string</returns>
static string ConvertCaseString(string phrase, Case cases)
{
string[] splittedPhrase = phrase.Split(' ', '-', '.');
var sb = new StringBuilder();
if (cases == Case.CamelCase)
{
sb.Append(splittedPhrase[0].ToLower());
splittedPhrase[0] = string.Empty;
}
else if (cases == Case.PascalCase)
sb = new StringBuilder();
foreach (String s in splittedPhrase)
{
char[] splittedPhraseChars = s.ToCharArray();
if (splittedPhraseChars.Length > 0)
{
splittedPhraseChars[0] = ((new String(splittedPhraseChars[0], 1)).ToUpper().ToCharArray())[0];
}
sb.Append(new String(splittedPhraseChars));
}
return sb.ToString();
}
enum Case
{
PascalCase,
CamelCase
}
You could just split it and put it back together:
string[] split = ("North Korea").Split(' ');
StringBuilder sb = new StringBuilder();
for (int i = 0; i < split.Count(); i++)
{
if (i == 0)
sb.Append(split[i].ToLower());
else
sb.Append(split[i]);
}
Edit: Switched to a StringBuilder instead, like Bazzz suggested.
This builds on Paolo Falabella's answer as a String extension and handles a few boundary cases such as empty string. Since there is some confusion between CamelCase and camelCase, I called it LowerCamelCase as described on Wikipedia. I resisted the temptation to go with nerdCaps.
internal static string ToLowerCamelCase( this string input )
{
string output = "";
if( String.IsNullOrEmpty( input ) == false )
{
output = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase( input ); //in case not Title Case
output = output.Replace( " ", "" ); //remove any white spaces between words
if( String.IsNullOrEmpty( output ) == false ) //handles the case where input is " "
{
output = char.ToLower( output[0] ) + output.Substring( 1 ); //lowercase first (even if 1 character string)
}
}
return output;
}
Use:
string test = "Foo Bar";
test = test.ToLowerCamelCase();
... //test is now "fooBar"
Update:
toong raised a good point in the comments - this will not work for graphemes. See the link provided by toong. There are also examples of iterating graphemes here and here if you want to tweak the above code for graphemes.
String::Split definitely is one of my pet peeves. Also, none of the other answers deal with:
Cultures
All forms of word seperators
Numbers
What happens when it starts with word seperators
I tried to get it as close as possible to what you would find in base class library code:
static string ToCamelCaseInvariant(string value) { return ToCamelCase(value, true, CultureInfo.InvariantCulture); }
static string ToCamelCaseInvariant(string value, bool changeWordCaps) { return ToCamelCase(value, changeWordCaps, CultureInfo.InvariantCulture); }
static string ToCamelCase(string value) { return ToCamelCase(value, true, CultureInfo.CurrentCulture); }
static string ToCamelCase(string value, bool changeWordCaps) { return ToCamelCase(value, changeWordCaps, CultureInfo.CurrentCulture); }
/// <summary>
/// Converts the given string value into camelCase.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="changeWordCaps">If set to <c>true</c> letters in a word (apart from the first) will be lowercased.</param>
/// <param name="culture">The culture to use to change the case of the characters.</param>
/// <returns>
/// The camel case value.
/// </returns>
static string ToCamelCase(string value, bool changeWordCaps, CultureInfo culture)
{
if (culture == null)
throw new ArgumentNullException("culture");
if (string.IsNullOrEmpty(value))
return value;
var result = new StringBuilder(value.Length);
var lastWasBreak = true;
for (var i = 0; i < value.Length; i++)
{
var c = value[i];
if (char.IsWhiteSpace(c) || char.IsPunctuation(c) || char.IsSeparator(c))
{
lastWasBreak = true;
}
else if (char.IsNumber(c))
{
result.Append(c);
lastWasBreak = true;
}
else
{
if (result.Length == 0)
{
result.Append(char.ToLower(c, culture));
}
else if (lastWasBreak)
{
result.Append(char.ToUpper(c, culture));
}
else if (changeWordCaps)
{
result.Append(char.ToLower(c, culture));
}
else
{
result.Append(c);
}
lastWasBreak = false;
}
}
return result.ToString();
}
// Tests
' This is a test. 12345hello world' = 'thisIsATest12345HelloWorld'
'--north korea' = 'northKorea'
'!nOrTH koreA' = 'northKorea'
'System.Console.' = 'systemConsole'
Try the following:
var input = "Hi my name is Rony";
var subStrs = input.ToLower().Split(' ');
var output = "";
foreach(var s in subStrs)
{
if(s!=subStrs[0])
output += s.First().ToString().ToUpper() + String.Join("", s.Skip(1));
else
output += s;
}
should get "hiMyNameIsRony" as the output
string toCamelCase(string s)
{
if (s.Length < 2) return s.ToLower();
return Char.ToLowerInvariant(s[0]) + s.Substring(1);
}
similar to Paolo Falabella's code but survives empty strings and 1 char strings.

escaping tricky string to CSV format

I have to create a CSV file from webservice output and the CSV file uses quoted strings with comma separator. I cannot change the format...
So if I have a string it becomes a "string"...
If the value has quotes already they are replaced with double quotes.
For example a str"ing becomes "str""ing"...
However, lately my import has been failing because of the following
original input string is: "","word1,word2,..."
every single quote is replaced by double resulting in: """",""word1,word2,...""
then its prefixed and suffixed with quote before written to CVS file: """"",""word1,word2,..."""
As you can see the final result is this:
""""",""word1,word2,..."""
which breaks my import (is sees it as another field)...
I think the issue is appereance of "," in the original input string.
Is there a CVS escape sequence for this scenario?
Update
The reason why above breaks is due to BCP mapping file (BCP utility is used to load CSV file into SQL db) which has terminator defined as "," . So instead of seeing 1 field it sees 2...But I cannot change the mapping file...
I use this code and it has always worked:
/// <summary>
/// Turn a string into a CSV cell output
/// </summary>
/// <param name="str">String to output</param>
/// <returns>The CSV cell formatted string</returns>
public static string StringToCSVCell(string str)
{
bool mustQuote = (str.Contains(",") || str.Contains("\"") || str.Contains("\r") || str.Contains("\n"));
if (mustQuote)
{
StringBuilder sb = new StringBuilder();
sb.Append("\"");
foreach (char nextChar in str)
{
sb.Append(nextChar);
if (nextChar == '"')
sb.Append("\"");
}
sb.Append("\"");
return sb.ToString();
}
return str;
}
Based on Ed Bayiates' answer:
/// <summary>
/// Turn a string into a CSV cell output
/// </summary>
/// <param name="value">String to output</param>
/// <returns>The CSV cell formatted string</returns>
private string ConvertToCsvCell(string value)
{
var mustQuote = value.Any(x => x == ',' || x == '\"' || x == '\r' || x == '\n');
if (!mustQuote)
{
return value;
}
value = value.Replace("\"", "\"\"");
return string.Format("\"{0}\"", value);
}
My penny thought:
String[] lines = new String[] { "\"\",\"word\",word,word2,1,34,5,2,\"details\"" };
for (int j = 0; j < lines.Length; j++)
{
String[] fields=lines[j].Split(',');
for (int i =0; i<fields.Length; i++)
{
if (fields[i].StartsWith("\"") && fields[i].EndsWith("\""))
{
char[] tmp = new char[fields[i].Length-2];
fields[i].CopyTo(1,tmp,0,fields[i].Length-2);
fields[i] =tmp.ToString();
fields[i] = "\""+fields[i].Replace("\"","\"\"")+"\"";
}
else
fields[i] = fields[i].Replace("\"","\"\"");
}
lines[j]=String.Join(",",fields);
}
Based on contribution of "Ed Bayiates" here's an helpful class to buid csv document:
/// <summary>
/// helpful class to build csv document
/// </summary>
public class CsvBuilder
{
/// <summary>
/// create the csv builder
/// </summary>
public CsvBuilder(char csvSeparator)
{
m_csvSeparator = csvSeparator;
}
/// <summary>
/// append a cell
/// </summary>
public void appendCell(string strCellValue)
{
if (m_nCurrentColumnIndex > 0) m_strBuilder.Append(m_csvSeparator);
bool mustQuote = (strCellValue.Contains(m_csvSeparator)
|| strCellValue.Contains('\"')
|| strCellValue.Contains('\r')
|| strCellValue.Contains('\n'));
if (mustQuote)
{
m_strBuilder.Append('\"');
foreach (char nextChar in strCellValue)
{
m_strBuilder.Append(nextChar);
if (nextChar == '"') m_strBuilder.Append('\"');
}
m_strBuilder.Append('\"');
}
else
{
m_strBuilder.Append(strCellValue);
}
m_nCurrentColumnIndex++;
}
/// <summary>
/// end of line, new line
/// </summary>
public void appendNewLine()
{
m_strBuilder.Append(Environment.NewLine);
m_nCurrentColumnIndex = 0;
}
/// <summary>
/// Create the CSV file
/// </summary>
/// <param name="path"></param>
public void save(string path )
{
File.WriteAllText(path, ToString());
}
public override string ToString()
{
return m_strBuilder.ToString();
}
private StringBuilder m_strBuilder = new StringBuilder();
private char m_csvSeparator;
private int m_nCurrentColumnIndex = 0;
}
How to use it:
void exportAsCsv( string strFileName )
{
CsvBuilder csvStringBuilder = new CsvBuilder(';');
csvStringBuilder.appendCell("#Header col 1 : Name");
csvStringBuilder.appendCell("col 2 : Value");
csvStringBuilder.appendNewLine();
foreach (Data data in m_dataSet)
{
csvStringBuilder.appendCell(data.getName());
csvStringBuilder.appendCell(data.getValue());
csvStringBuilder.appendNewLine();
}
csvStringBuilder.save(strFileName);
}
the first step in parsing this is removing the extra added " 's around your string. Once you do this, you should be able to deal with the embedded " as well as the ,'s.
After much deliberation, it was decided that import utility format was needed to be fixed. The escaping of the string was correct (as users indicated) but the format file that import utility used was incorrect and was causing it to break import.
Thanks all and special thanks to #dbt (up vote)

Linq - lookahead Iteration

I am iterating thru a collection using a visitor-type pattern and need to access the current and next item in the list. At the moment I am doing it via an extension method like this
public void Visit<TItem>(this IEnumerable<TItem> theList, Action<TItem, TItem> visitor)
{
for (i = 0; i <= theList.Count - 1; i++) {
if (i == theList.Count - 1) {
visitor(theList(i), null);
} else {
visitor(theList(i), theList(i + 1));
}
}
}
I was wondering whether there are other/better/more elegant ways to achieve this? At the moment I think I only need to have access to the current and next items in the list, but I'm wondering whether I may encounter situations where I may need to lookahead the next 'n' items, for example.
Assuming you're using .NET 4, you can use Zip to accomplish the same thing:
var query = original.Zip(original.Skip(1),
(current, next) => new { current, next });
This will iterate over the sequence twice though. A nicer alternative to your current extension method (which I don't believe will work, btw, as IEnumerable doesn't have a Count property, and you're trying to call theList as a method as well...) would be something like:
public static void Visit<TItem>(this IEnumerable<TItem> theList,
Action<TItem, TItem> visitor)
{
TItem prev = default(TItem);
using (var iterator = theList.GetEnumerator())
{
if (!iterator.MoveNext())
{
return;
}
prev = iterator.Current;
while (iterator.MoveNext())
{
TItem current = iterator.Current;
visitor(prev, current);
prev = current;
}
}
visitor(prev, default(TItem)); // Are you sure you want this?
}
A more general lookahead is trickier, to be honest... you'd want some sort of circular buffer, I suspect... probably a custom collection.
When we run into a similar task we have defined an extension methods:
/// <summary>
/// Projects a window of source elements in a source sequence into target sequence.
/// Thus
/// target[i] =
/// selector(source[i], source[i - 1], ... source[i - window + 1])
/// </summary>
/// <typeparam name="T">A type of elements of source sequence.</typeparam>
/// <typeparam name="R">A type of elements of target sequence.</typeparam>
/// <param name="source">A source sequence.</param>
/// <param name="window">A size of window.</param>
/// <param name="lookbehind">
/// Indicate whether to produce target if the number of source elements
/// preceeding the current is less than the window size.
/// </param>
/// <param name="lookahead">
/// Indicate whether to produce target if the number of source elements
/// following current is less than the window size.
/// </param>
/// <param name="selector">
/// A selector that derives target element.
/// On input it receives:
/// an array of source elements stored in round-robing fashon;
/// an index of the first element;
/// a number of elements in the array to count.
/// </param>
/// <returns>Returns a sequence of target elements.</returns>
public static IEnumerable<R> Window<T, R>(
this IEnumerable<T> source,
int window,
bool lookbehind,
bool lookahead,
Func<T[], int, int, R> selector)
{
var buffer = new T[window];
var index = 0;
var count = 0;
foreach(var value in source)
{
if (count < window)
{
buffer[count++] = value;
if (lookbehind || (count == window))
{
yield return selector(buffer, 0, count);
}
}
else
{
buffer[index] = value;
index = index + 1 == window ? 0 : index + 1;
yield return selector(buffer, index, count);
}
}
if (lookahead)
{
while(--count > 0)
{
index = index + 1 == window ? 0 : index + 1;
yield return selector(buffer, index, count);
}
}
}
/// <summary>
/// Projects a window of source elements in a source sequence into a
/// sequence of window arrays.
/// </summary>
/// <typeparam name="T">A type of elements of source sequence.</typeparam>
/// <typeparam name="R">A type of elements of target sequence.</typeparam>
/// <param name="source">A source sequence.</param>
/// <param name="window">A size of window.</param>
/// <param name="lookbehind">
/// Indicate whether to produce target if the number of source elements
/// preceeding the current is less than the window size.
/// </param>
/// <param name="lookahead">
/// Indicate whether to produce target if the number of source elements
/// following current is less than the window size.
/// </param>
/// <returns>Returns a sequence of windows.</returns>
public static IEnumerable<T[]> Window<T>(
this IEnumerable<T> source,
int window,
bool lookbehind,
bool lookahead)
{
return source.Window(
window,
lookbehind,
lookahead,
(buffer, index, count) =>
{
var result = new T[count];
for(var i = 0; i < count; ++i)
{
result[i] = buffer[index];
index = index + 1 == buffer.Length ? 0 : index + 1;
}
return result;
});
}
These functions help to produce output elements from a window of input elements.
See also LINQ extensions.
It seems like you are using the wrong type. The act of indexing an sequence will iterate it until it reaches the specified index every single time. Why not use IList<T> or ReadOnlyCollection<T>?
Not tested, but I think this works? When the visit would exceed the bounds it loops to the front of the list.
public class FriendlyEnumerable<T> : IEnumerable<T>
{
private IEnumerable<T> _enum;
public FriendlyEnumerable(IEnumerable<T> enumerable)
{
_enum = enumerable;
}
public void VisitAll(Action<T, T> visitFunc)
{
VisitAll(visitFunc, 1);
}
public void VisitAll(Action<T, T> visitFunc, int lookahead)
{
int index = 0;
int length = _enum.Count();
_enum.ToList().ForEach(t =>
{
for (int i = 1; i <= lookahead; i++)
visitFunc(t, _enum.ElementAt((index + i) % length));
index++;
});
}
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
return _enum.GetEnumerator();
}
#endregion
}
You could use it like:
List<string> results = new List<string>();
List<string> strings = new List<string>()
{ "a", "b", "c", "d", "a", "b", "c", "d" };
FriendlyEnumerable<string> fe = new FriendlyEnumerable<string>(strings);
Action<string, string> compareString =
new Action<string,string>((s1, s2) =>
{
if (s1 == s2)
results.Add(s1 + " == " + s2);
});
fe.VisitAll(compareString);
//no results
fe.VisitAll(compareString, 4);
//8 results
public static void VisitLookAhead<TItem>(
this IEnumerable<TItem> source,
Action<IEnumerable<TItem>> visitor,
int targetSize
)
{
if (targetSize <= 1)
{
throw new Exception("invalid targetSize for VisitLookAhead");
}
List<List<TItem>> collections = new List<List<TItem>>();
// after 6th iteration with targetSize 6
//1, 2, 3, 4, 5, 6 <-- foundlist
//2, 3, 4, 5, 6
//3, 4, 5, 6
//4, 5, 6
//5, 6
//6
foreach(TItem x in source)
{
collections.Add(new List<TItem>());
collections.ForEach(subList => subList.Add(x));
List<TItem> foundList = collections
.FirstOrDefault(subList => subList.Count == targetSize);
if (foundList != null)
{
collections.Remove(foundList);
visitor(foundList);
}
}
//generate extra lists at the end - when lookahead will be missing items.
foreach(int i in Enumerable.Range(1, targetSize)
{
collections.ForEach(subList => subList.Add(default(TItem)));
List<TItem> foundList = collections
.FirstOrDefault(subList => subList.Count == targetSize);
if (foundList != null)
{
collections.Remove(foundList);
visitor(foundList);
}
}
}

Categories