How to replace one character with multiple characters c# - c#

I am doing some exapmples for my exam, and i find a problem replacing string with other string. I know how to replace one character in string to others or revert a string.
We have a one string
s="*****\n***\n****"
and we need to replace all "*" with "asd", and it should look like this
s="asdas\ndas\ndasd"

One possible approach is to write a for loop and check if the character is a * an keep track using a counter how many replacements have already been done.
Make use of the modulo % to see which character you need for the replacement and use a StringBuilder to add the characters.
For example
string s="*****\n***\n****";
StringBuilder sb = new StringBuilder();
string subj = "asd";
int counter = 0;
for (int i = 0; i < s.Length; i++) {
if (s[i] == '*') {
sb.Append(subj[counter % subj.Length]);
counter++;
continue;
}
sb.Append(s[i]);
}
Console.WriteLine(sb.ToString());
Result
asdas
das
dasd
C# demo

This does sound like a Homework assignment and with those we avoid providing full code. It is way to important for you to do the coding yourself. But it comes down to
Itterate over the string until you run out of '*' characters:
Replace the first '*' with 'a'.
Replace the first '*' with 's'.
Replace teh first '*' with 'd'
Restart at 1
There is one important thing to note with strings: Strings are inmutable. You can not modify a string, only create one with a different value. Each replace will create a new, dead string in memory. For this scope it does not mater, but for anything larger it may. Consider changing a Char[] or StringBuilder rather then a raw string, the recreate a string.

Related

C# - Auto-detect Escape and Argument curly braces in string.Format

starting from this C# example:
string myStringFormat = "I want to surround string {0} with {{ and }}";
string myStringArgs = "StringToSurround";
string myFinalString = string.Format(myStringFormat, myStringArgs);
I'd like to know if there is a quick and simple way to distinguish between escape character/sequence and arguments for curly braces/brackets.
The reasons why I am asking this are:
+] I want to provide some logging functionality and I don't want to require users to be aware of the double curly braces/brackets escape rule
+] I want to be very fast in doing this distinction for performance requirements
Currently the only solution I can think about is to scan the string looking for curly braces/brackets and do some check (number parsing) on subsequent characters. Probably regex can be helpful but I cannot find a way to use them in this scenario.
Btw, the final situation I'd like to achieve is user being allowed to this without getting exceptions:
string myStringFormat = "I want to surround string {0} with { and }";
string myStringArgs = "StringToSurround";
//string myFinalString = string.Format(myStringFormat, myStringArgs); throwing exception
string myFinalString = MyCustomizedStringFormat(myStringFormat, myStringArgs);
EDIT:
sorry the word "surround" was tricky and misleading, please consider this example:
string myStringFormat = "I want to append to string {0} these characters {{ and }}";
string myStringArgs = "StringToAppendTo";
string myFinalString = string.Format(myStringFormat, myStringArgs);
giving output
I want to append to string StringToAppendTo these characters { and }
Use this Regex to find the Argument substrings:
{\d+}
This regex escapes {} {1a} etc. and only chooses {1} {11} etc.
Now you need to handle either Arguments (replace them with their values) or the Escaped curly braces (replace them with double braces). The choice is yours and it depends on the number of occurrences of each case. (I chose to replace arguments in my code below)
Now you need to actually replace the characters. Again the choice is yours to use a StringBuilder or not. It depends on the size of your input and the number of replacements. In any case I suggest StringBuilder.
var m = Regex.Matches(input, #"{\d+}");
if (m.Any())
{
// before any arg
var sb = new StringBuilder(input.Substring(0, m[0].Index));
for (int i = 0; i < m.Count; i++)
{
// arg itself
sb.Append(args[i]);
// from right after arg
int start = m[i].Index + m[i].Value.Length;
if (i < m.Count - 1)
{
// until next arg
int length = m[i + 1].Index - start;
sb.Append(input.Substring(start, length));
}
else
// until end of input
sb.Append(input.Substring(start));
}
}
I believe this is the most robust and cleanest way to do it,and it does not have any performance (memory or speed) issues.
Edit
If you don't have access to args[] then you can first replace {/} with {{/}} and then simply do these modifications to the code:
use this pattern: #"{{\d+}}"
write m[i].Value.Substring(1, m[i].Value.Length - 2) instead of args[i]

Reversing a string sentence (not necessarily space between each)

I have this string:
com.example.is-this#myname
i would like it to be
myname#this-is.example.com
using .Net, but a straight out concept or an idea would be good to.
What i'm currently doing is going over each character, find out if it's one of the "special characters" and assign all prior chars, to a variable of an array, at the end, i'm joining them all together from last to first.
is there a possible more efficient way to do this ?
This is the classic word-by-word reversal, with a small twist on delimiters. A solution to this problem is reversing each word individually, and then reversing the whole string. Do not touch delimiters when reversing words.
First step goes as follows: we find limits of each token, and reverse it in place, like this:
com.example.is-this#myname
moc.example.is-this#myname
moc.elpmaxe.is-this#myname
moc.elpmaxe.si-this#myname
moc.elpmaxe.si-siht#myname
moc.elpmaxe.si-siht#emanym
Reverse the result to get your desired output:
moc.elpmaxe.si-siht#emanym -> myname#this-is.example.com
As far as the implementation goes, you can do it by converting the string to an array of characters to make it changeable in place, and write a short helper method that lets you reverse a portion of a char array between indexes i and j. With this helper method in place, all you need to do is to find delimiters and call the helper for each delimited word, and then make one final call to reverse the entire sentence.
With little bit of Regex and Linq this is fairly simple.
Idea is that we take words and non word characters as separate token with Regex patten. Then, we just reverse it and join it.
var tokens = Regex.Matches("com.example.is-this#myname", #"\w+|\W")
.Cast<Match>()
.Select(x=>x.Value)
.Reverse();
string reversed = string.Concat(tokens);
Output: Ideone - Demo
myname#this-is.example.com
You could use the Split C# method.
The example below is from here.
using System;
class Program
{
static void Main()
{
string s = "there is a cat";
// Split string on spaces.
// ... This will separate all the words.
string[] words = s.Split(' ');
foreach (string word in words)
{
Console.WriteLine(word);
}
}
}
Is as simple as examples get.
Then you add more conditions to your Split()
string [] split = strings .Split(new Char [] {'.' , '#', '-' },
StringSplitOptions.RemoveEmptyEntries);
The RemoveEmptyEntries just removes unwanted empty entries to your array.
After that you reverse your array using the Array.Reverse method.
And then you can stitch your string back together with a Foreach loop.
As #marjan-venema mentioned in the comments you could populate a parallel array at this point with each delimiter. Reverse it, and then concatenate the string when you are using the Foreach loop at each entry.
Here's another way to do it using a List, which has a handy Insert method, as well as a Reverse method.
The Insert method lets you continue to add characters to the same index in the list (and the others after it are moved to higher indexes).
So, as you read the original string, you can keep inserting the characters at the start. Once you come to a delimeter, you add it to the end and adjust your insert position to be right after the delimeter.
When you're done, you just call Reverse and join the characters back to a string:
public static string ReverseWords(string words, char[] wordDelimeters)
{
var reversed = new List<char>();
int insertPosition = 0;
for(int i = 0; i < words.Length; i++)
{
var character = words[i];
if (wordDelimeters.Contains(character))
{
reversed.Add(character);
insertPosition = i + 1;
continue;
}
reversed.Insert(insertPosition, character);
}
reversed.Reverse();
return string.Join("", reversed);
}

Using string.ToUpper on substring

Have an assignment to allow a user to input a word in C# and then display that word with the first and third characters changed to uppercase. Code follows:
namespace Capitalizer
{
class Program
{
static void Main(string[] args)
{
string text = Console.ReadLine();
char[] delimiterChars = { ' ' };
string[] words = text.Split(delimiterChars);
string Upper = text.ToUpper();
Console.WriteLine(Upper);
Console.ReadKey();
}
}
}
This of course generates the entire word in uppercase, which is not what I want. I can't seem to make text.ToUpper(0,2) work, and even then that'd capitalize the first three letters. Only solution I can think of now that would make the word appear on one line (and I don't know if it works) is to move the capitalized letters and lowercase letters into a character array and try to get that to print all values in a modified order.
The simplest way I can think of to address your exact question as described — to convert to upper case the first and third characters of the input — would be something like the following:
StringBuilder sb = new StringBuilder(text);
sb[0] = char.ToUpper(sb[0]);
sb[2] = char.ToUpper(sb[2]);
text = sb.ToString();
The StringBuilder class is essentially a mutable string object, so when doing these kinds of operations is the most fluid way to approach the problem, as it provides the most straightforward conversions to and from, as well as the full range of string operations. Changing individual characters is easy in many data structures, but insertions, deletions, appending, formatting, etc. all also come with StringBuilder, so it's a good habit to use that versus other approaches.
But frankly, it's hard to see how that's a useful operation. I can't help but wonder if you have stated the requirements incorrectly and there's something more to this question than is seen here.
You could use LINQ:
var upperCaseIndices = new[] { 0, 2 };
var message = "hello";
var newMessage = new string(message.Select((c, i) =>
upperCaseIndices.Contains(i) ? Char.ToUpper(c) : c).ToArray());
Here is how it works. message.Select (inline LINQ query) selects characters from message one by one and passes into selector function:
upperCaseIndices.Contains(i) ? Char.ToUpper(c) : c
written as C# ?: shorthand syntax for if. It reads as "If index is present in the array, then select upper case character. Otherwise select character as is."
(c, i) => condition
is a lambda expression. See also:
Understand Lambda Expressions in 3 minutes
The rest is very simple - represent result as array of characters (.ToArray()), and create a new string based off that (new string(...)).
Only solution I can think of now that would make the word appear on one line (and I don't know if it works) is to move the capitalized letters and lowercase letters into a character array and try to get that to print all values in a modified order.
That seems a lot more complicated than necessary. Once you have a character array, you can simply change the elements of that character array. In a separate function, it would look something like
string MakeFirstAndThirdCharacterUppercase(string word) {
var chars = word.ToCharArray();
chars[0] = chars[0].ToUpper();
chars[2] = chars[2].ToUpper();
return new string(chars);
}
My simple solution:
string text = Console.ReadLine();
char[] delimiterChars = { ' ' };
string[] words = text.Split(delimiterChars);
foreach (string s in words)
{
char[] chars = s.ToCharArray();
chars[0] = char.ToUpper(chars[0]);
if (chars.Length > 2)
{
chars[2] = char.ToUpper(chars[2]);
}
Console.Write(new string(chars));
Console.Write(' ');
}
Console.ReadKey();

Cut the string to be <= 80 characters AND must keep the words without cutting them

I am new to C#, but I have a requirement to cut the strings to be <= 80 characters AND they must keep the words integrity (without cutting them)
Examples
Before: I have a requirenment to cut the strings to be <= 80 characters AND must keep the words without cutting them (length=108)
After: I have a requirenment to cut the strings to be <= 80 characters AND must keep (length=77)
Before: requirenment to cut the strings to be <= 80 characters AND must keep the words without cutting them (length=99)
After: requirenment to cut the strings to be <= 80 characters AND must keep the words (length=78)
Before: I have a requirenment the strings to be <= 80 characters AND must keep the words without cutting them (length=101)
After: I have a requirenment the strings to be <= 80 characters AND must keep the words (length=80)
I want to use the RegEx, but I don't know anything about the regex. It would be a hassle to to the else-if's for this.
I would appreciate if you could point me to the right article which I could use to create this expression.
this is my function that I want to cut to one line:
public String cutTitleto80(String s){
String[] words = Regex.Split(s, "\\s+");
String finalResult = "";
foreach (String word in words)
{
String tmp = finalResult + " " + word;
if (tmp.Length > 80)
{
return finalResult;
}
finalResult = tmp;
}
return finalResult;
}
Try
^(.{0,80})(?: |$)
This is a capturing greedy match which must be followed by a space or end of string. You could also use a zero-width lookahead assertion, as in
^.{0,80}(?= |$)
If you use a live test tool like http://regexhero.net/tester/ it's pretty cool, you can actually see it jump back to the word boundary as you type beyond 80 characters.
And here's one which will simply truncate at the 80th character if there are no word boundaries (spaces) to be found:
^(.{1,80}(?: |$)|.{80})
Here's an approach without using Regex: just split the string (however you'd like) into whatever you consider "words" to be. Then, just start concatenating them together using a StringBuilder, checking for your desired length, until you can't add the next "word". Then, just return the string that you have built up so far.
(Untested code ahead)
public string TruncateWithPreservation(string s, int len)
{
string[] parts = s.Split(' ');
StringBuilder sb = new StringBuilder();
foreach (string part in parts)
{
if (sb.Length + part.Length > len)
break;
sb.Append(' ');
sb.Append(part);
}
return sb.ToString();
}
string truncatedText = text.Substring(0, 80); // truncate to 80 characters
if (text[80] != ' ') // don't remove last word if a space occurs after it in the original string (in other words, last word is already complete)
truncatedText = truncatedText.Substring(0, truncatedText.LastIndexOf(' ')); // remove any cut-off words
Updated to fix issue from comments where last word could get cut off even if it is complete.
This isn't using regex but this is how I would do it:
Use String.LastIndexOf to get the last space before the 81st char.
If the 81th char is a space then take it until 80.
if it returns a number > -1 cut it off there.
If it's -1 you-have-a-really-long-word-or-someone-messing-with-the-system so you do wathever you like.

How to remove words based on a word count

Here is what I'm trying to accomplish. I have an object coming back from
the database with a string description. This description can be up to 1000
characters long, but we only want to display a short view of this. So I coded
up the following, but I'm having trouble in actually removing the number of
words after the regular expression finds the total count of words. Does anyone
have good way of dispalying the words which are less than the Regex.Matches?
Thanks!
if (!string.IsNullOrEmpty(myObject.Description))
{
string original = myObject.Description;
MatchCollection wordColl = Regex.Matches(original, #"[\S]+");
if (wordColl.Count < 70) // 70 words?
{
uxDescriptionDisplay.Text =
string.Format("<p>{0}</p>", myObject.Description);
}
else
{
string shortendText = original.Remove(200); // 200 characters?
uxDescriptionDisplay.Text =
string.Format("<p>{0}</p>", shortendText);
}
}
EDIT:
So this is what I got working on my own:
else
{
int count = 0;
StringBuilder builder = new StringBuilder();
string[] workingText = original.Split(' ');
foreach (string word in workingText)
{
if (count < 70)
{
builder.AppendFormat("{0} ", word);
}
count++;
}
string shortendText = builder.ToString();
}
It's not pretty, but it worked. I would call it a pretty naive way of doing this. Thanks for all of the suggestions!
I would opt to go by a strict character count rather than a word count because you might happen to have a lot of long words.
I might do something like (pseudocode)
if text.Length > someLimit
find first whitespace after someLimit (or perhaps last whitespace immediately before)
display substring of text
else
display text
Possible code implementation:
string TruncateText(string input, int characterLimit)
{
if (input.Length > characterLimit)
{
// find last whitespace immediately before limit
int whitespacePosition = input.Substring(0, characterLimit).LastIndexOf(" ");
// or find first whitespace after limit (what is spec?)
// int whitespacePosition = input.IndexOf(" ", characterLimit);
if (whitespacePosition > -1)
return input.Substring(0, whitespacePosition);
}
return input;
}
One method, if you're using at least C#3.0, would be a LINQ like the following. This is provided you're going strictly by word count, not character count.
if (wordColl.Count > 70)
{
foreach (var subWord in wordColl.Cast<Match>().Select(r => r.Value).Take(70))
{
//Build string here out of subWord
}
}
I did a test using a simple Console.WriteLine with your Regex and your question body (which is over 70 words, it turns out).
You can use Regex Capture Groups to hold the match and access it later.
For your application, I'd recommend instead simply splitting the string by spaces and returning the first n elements of the array:
if (!string.IsNullOrEmpty(myObject.Description))
{
string original = myObject.Description;
string[] words = original.Split(' ');
if (words.Length < 70)
{
uxDescriptionDisplay.Text =
string.Format("<p>{0}</p>", original);
}
else
{
string shortDesc = string.Empty;
for(int i = 0; i < 70; i++) shortDesc += words[i] + " ";
uxDescriptionDisplay.Text =
string.Format("<p>{0}</p>", shortDesc.Trim());
}
}
Are you wanting to remove 200 characters or start truncating at the 200th character? When you call original.Remove(200) you are indexing the start of the truncation at the 200th character. This is how you use Remove() for a certain number of characters to remove:
string shortendText = original.Remove(0,200);
This starts at the first character and removes 200 starting with that one. Which I imagine that's not what you're trying to do since you're shortening a description. That's merely the correct way to use Remove().
Instead of using Regex matchcollections why not just split the string? It's a lot easier and straight forward. You can set the delimiter to a space character and split that way. Not sure if that completely fixes your need but it just might. I'm not sure what your data looks like in the description. But you split this way:
String[] wordArray = original.Split(' ');
From there you can determine the word count with wordArray's Length property value.
If I was you I would go by characters as you may have many one letter words or many long words in your text.
Go through until characters <= your limit, then either find the next space and then add these characters to a new string (possibly using the SubString method) or take these characters and add a few full stops, then make a new string The later could be unproffessional I suppose.

Categories

Resources