How to implement "Find, Replace, Next" in a String on C#? - c#

I'm searching for a solution to this case:
I have a Method inside a DLL that receive a string that contains some words as "placeholders/parameters" that will be replaced by a result of another specific method (inside dll too)
Too simplificate: It's a query string received as an argument to be on a method inside a DLL, where X word that matchs a specifc case, will be replaced.
My method receive a string that could be like this:
(on .exe app)
string str = "INSERT INTO mydb.mytable (id_field, description, complex_number) VALUES ('#GEN_COMPLEX_ID#','A complex solution', '#GEN_COMPLEX_ID#');"
MyDLLClass.MyMethod(str);
So, the problem is: if i replace the #GEN_COMPLEX_ID# on this string, wanting that a different should be on each match, it not will happen because the replaced executes the function in a single shot (not step by step). So, i wanna help to implement this: a step by step replace of any text (like Find some word, replace, than next ... replace ... next... etc.
Could you help me?
Thanks!

This works pretty well for me:
string yourOriginalString = "ab cd ab cd ab cd";
string pattern = "ab";
string yourNewDescription = "123";
int startingPositionOffset = 0;
int yourOriginalStringLength = yourOriginalString.Length;
MatchCollection match = Regex.Matches(yourOriginalString, pattern, RegexOptions.IgnoreCase | RegexOptions.Multiline);
foreach (Match m in match)
{
yourOriginalString = yourOriginalString.Substring(0, m.Index+startingPositionOffset) + yourNewDescription + yourOriginalString.Substring(m.Index + startingPositionOffset+ m.Length);
startingPositionOffset = yourOriginalString.Length - yourOriginalStringLength;
}

If what you're asking is how to replace each placeholder with a different value, you can do it using the Regex.Replace overload which accepts a MatchEvaluator delegate, and executes it for each match:
// conceptually, something like this (note that it's not checking if there are
// enough values in the replacementValues array)
static string ReplaceMultiple(
string input, string placeholder, IEnumerable<string> replacementValues)
{
var enumerator = replacementValues.GetEnumerator();
return Regex.Replace(input, placeholder,
m => { enumerator.MoveNext(); return enumerator.Current; });
}
This is, of course, presuming that all placeholders look the same.

Pseudo-code
var split = source.Split(placeholder); // create array of items without placeholders
var result = split[0]; // copy first item
for(int i = 1; i < result.Length; i++)
{
bool replace = ... // ask user
result += replace ? replacement : placeholder; // to put replacement or not to put
result += split[i]; // copy next item
}

you should use the split method like this
string [] placeholder = {"#Placeholder#"} ;
string[] request = cd.Split(placeholder, StringSplitOptions.RemoveEmptyEntries);
StringBuilder requetBuilding = new StringBuilder();
requetBuilding.Append(request[0]);
int index = 1;
requetBuilding.Append("Your place holder replacement");
requetBuilding.Append(request[index]);
index++; //next replacement
// requetBuilding.Append("Your next place holder replacement");
// requetBuilding.Append(request[index]);

Related

Trying to make substrings from a string in C#

I have a string aaaaabbbbbccccc I have a dictionary that has certain rules
Dictionary<string, string> rules = new Dictionary<string, string>();
rules.Add("abc", "aab");
rules.Add("ac", "ba");
rules.Add("cb", "cc");
This means if string has abc it will be replaced with aab but string doesn't have any rules that match. So, I am creating a new string out of the old string based on these rules. For instance, if I rearrange the old string to abcabcabcabcabc then rule can be applied. But I am stuck at rearranging. I tried using IndexOf and Remove functions but I didn't get the positive output. This is my code
string s;
s = "aaaaabbbbbccccc";
string newString = "";
int ia, ib, ic;
//Formulating rule 1
if (s.Contains("a") && s.Contains("b") && s.Contains("c"))
{
ia = s.IndexOf("a");
ib = s.IndexOf("b");
ic = s.IndexOf("c");
if (ia < ib && ib < ic)
{
newString += "abc";
s.Remove(ia, 1);
s.Remove(ib, 1);
s.Remove(ic, 1);
}
}
Console.WriteLine("New String " + newString);
Console.WriteLine("Old String " + s);
I am getting
New String abc
Old String aaaaabbbbbccccc //Which is wrong.
Can anyone help what I am doing wrong or is there any better way?
Your explanation does not match your code.
In your explanation, you say that aaaaabbbbbccccc does not match any rule (e.g. the "abc" rule). However, in your code, you are not checking if it contains "abc", but rather that it contain "a" and "b" and "c", not necessarily as a single chunk:
if (s.Contains("a") && s.Contains("b") && s.Contains("c"))
This is an important difference:
aaaaabbbbbccccc does not contain abc (rule does not apply)
aaaaabbbbbccccc does contain a, b and c (rule does apply)
You're contradicting yourself. Which is correct here, the code or your explanation? Does the rule check for the exact string, or does it check for all characters separately?
Awkward string manipulation.
Based on your code; I infer that you're not experienced with some very common string operations (no offense intended). Unless you contradict me, I'm going to assume that your explanation is correct and your code is not.
Checking if a string contains a substring:
As I explained previously, there is an important difference between checking is a string contains a substring:
s.Contains("abc") //1
and checking if a string contains each individual character of a substring:
s.Contains("a") && s.Contains("b") && s.Contains("c") //2
As a more practical example; does my username (Flater) contain the substring "Fertla"?
If you use the logic in //1, the answer is no.
If you use the logic in //2, the answer is yes.
Based on your explanation, you should be using //1
Replacing a string:
This means if string has abc it will be replaced with aab
There is a very simple method for this:
s.Replace("abc", "aab");
Some examples:
abcdef becomes aabdef
abcabcabc becomes aabaabaab (it replaces all occurrences)
uvwxyz becomes uvwxyz (if it doesn't occur, nothing gets replaced)
Take note of the second and third bullet point.
String.Replace() will replace all occurrences, in case the substring occurs more than once. Based on your explanation, I assume this is what you want (if it's possible for a substring to occur more than once, to begin with).
If the substring is not found, String.Replace() will give you the same output as its input. Nothing will be changed. This means that you can execute your rule on your string without needing to check if the substring exists:
If it does exist, then your value will be replaced; just like you want it to happen.
If it does not exist, then nothing will happen; which is also what you want to happen.
You can dramatically simplify your code!
Create your dictionary;
Dictionary<string, string> rules = new Dictionary<string, string>();
rules.Add("abc", "aab");
rules.Add("ac", "ba");
rules.Add("cb", "cc");
Define your string:
string s = "aaaaaaabbbbbbccccccc";
And the rest is easy enough:
foreach(var rule in rules)
{
s = s.Replace(rule.Key, rule.Value);
}
This will try to perform a replace for every rule that you've defined. If it finds rules that are applicable; then it will replace the values.
Note
This code assumes that your replace values do not collide. If you do want to avoid collisions, you will have to check if substrings of all defined rules exist (Contains()) before actually replacing a value.
I have a really hard time understanding you requierment, but here is a solution may you tell me if this is even close to what you want?
private static string WeirdArrangement (string input)
{
//string input = "aabbcc[aczç_eyvur]";
string validChars = "abc";
string pattern = "abc"; // Must be a composition of all valid char
var invalidChars = input.Where(c => !validChars.Contains(c));
var validOccurences = input.Where(c => validChars.Contains(c))
.GroupBy(c => c)
.Select(c => new { Char = c.Key, Count = c.Count() });
var minPattern = validOccurences.Min(o => o.Count);
// Build time
StringBuilder builder = new StringBuilder();
//new StringBuilder(pattern.Length * minPattern + invalidChars.Count() + leftoverCount);
// X time partern
for (int i = 0; i < minPattern; i++) builder.Append(pattern);
//Rest of the validOccurences
foreach (var charOccurency in validOccurences)
{
for (int i = minPattern; i < charOccurency.Count; i++) builder.Append(charOccurency.Char);
}
//Invalid char
foreach (var invalidChar in invalidChars)
{
builder.Append(invalidChar);
};
return builder.ToString();
}

Parse a string in C#

I've the following string that I get from a method. I would like to parse it and make pairs. The order of input string will not change.
INPUT:
ku=value1,ku=value2,ku=value3,ku=value4,ku=value5,lu=value6,lu=value7,lu=value8,lu=value9
OUTPUT
Name value1
Title value2
School value3
.
.
.
Age value9
I think I can read through the string and assign value to the left hand side as I go and so on. However, I am very new to C#.
Use string.Split and split imput string to list key-value pair then split each pair to key and value. Tahts all.
You can do something like this:
void Main()
{
string str = "ku=value1,ku=value2,ku=value3,ku=value4,ku=value5,lu=value6,lu=value7,lu=value8,lu=value9";
var tempStr = str.Split(',');
var result = new List<KeyValue>();
foreach(var val in tempStr)
{
var temp = val.Split('=');
var kv = new KeyValue();
kv.Name = temp[0];
kv.Value = temp[1];
result.Add(kv);
}
}
public class KeyValue
{
public string Name {get;set;}
public string Value{get;set;}
}
If you don't need the first part you can do this using String split as follow
Split String on , using String.Split method creating sequence of string {"ku=value1","ku=value2",...}
Use Linq's Select method to apply an additional transformation
Use Split again on each item on the '=' character
Select the item to the right of the '=', at index 1 of the newly split item
Loop through everything and print your results
Here's the code
var target = "ku=value1,ku=value2,ku=value3,ku=value4,ku=value5,lu=value6,lu=value7,lu=value8,lu=value9";
var split = target.Split(',').Select(a=>a.Split('=')[1]).ToArray();
var names = new[]{"Name","Title","School",...,"Age"};
for(int i=0;i<split.Length;i++)
{
Console.WriteLine(names[i]+"\t"+split[i]);
}
If you want to find out more about how to use these methods you can look at the MSDN documentation for them :
String.Split(char[]) Method
Enumerable.Select Method
I suggest to try this way. Split() plus regular expression
string inputString = "ku=value1,ku=value2,ku=value3,ku=value4,ku=value5,lu=value6,lu=value7,lu=value8,lu=value9";
string pattern = "(.*)=(.*)";
foreach(var pair in inputString.Split(','))
{
var match = Regex.Match(pair,pattern);
Console.WriteLine(string.Format("{0} {1}",match.Groups[1].Value, match.Groups[2].Value));
}

Replacing characters in a string with another string

So what I am trying to do is as follows :
example of a string is A4PC
I am trying to replace for example any occurance of "A" with "[A4]" so I would get and similar any occurance of "4" with "[A4]"
"[A4][A4]PC"
I tried doing a normal Replace on the string but found out I got
"[A[A4]]PC"
string badWordAllVariants =
restriction.Value.Replace("A", "[A4]").Replace("4", "[A4]")
since I have two A's in a row causing an issue.
So I was thinking it would be better rather than the replace on the string I need to do it on a character per character basis and then build up a string again.
Is there anyway in Linq or so to do something like this ?
You don't need any LINQ here - String.Replace works just fine:
string input = "AAPC";
string result = input.Replace("A", "[A4]"); // "[A4][A4]PC"
UPDATE: For your updated requirements I suggest to use regular expression replace
string input = "A4PC";
var result = Regex.Replace(input, "A|4", "[A4]"); // "[A4][A4]PC"
This works well for me:
string x = "AAPC";
string replace = x.Replace("A", "[A4]");
EDIT:
Based on the updated question, the issue is the second replacement. In order to replace multiple strings you will want to do this sequentially:
var original = "AAPC";
// add arbitrary room to allow for more new characters
StringBuilder resultString = new StringBuilder(original.Length + 10);
foreach (char currentChar in original.ToCharArray())
{
if (currentChar == 'A') resultString.Append("[A4]");
else if (currentChar == '4') resultString.Append("[A4]");
else resultString.Append(currentChar);
}
string result = resultString.ToString();
You can run this routine with any replacements you want to make (in this case the letters 'A' and '4' and it should work. If you would want to replace strings the code would be similar in structure but you would need to "look ahead" and probably use a for loop. Hopefully this helps!
By the way - you want to use a string builder here and not strings because strings are static which means space gets allocated every time you loop. (Not good!)
I think this should do the trick
string str = "AA4PC";
string result = Regex.Replace(str, #"(?<Before>[^A4]?)(?<Value>A|4)(?<After>[^A4]?)", (m) =>
{
string before = m.Groups["Before"].Value;
string after = m.Groups["After"].Value;
string value = m.Groups["Value"].Value;
if (before != "[" || after != "]")
{
return "[A4]";
}
return m.ToString();
});
It is going to replace A and 4 that hasn't been replaced yet for [A4].

Efficient way to get part of a string

I have a name which is made of a prefix a type and name , i want to retrieve the name part.
Prefix always remains same but Type can change.
I have the following code to get the name part:
string prefix = "Prefix-";
string str =prefix + "Type-hello-j---.xml";
str = Path.GetFileNameWithoutExtension(str);
str = str.Substring(prefix.Length);
str = str.Substring(str.IndexOf('-') + 1);
In the above example the name part is: hello-j---
Is there any efficient/better way to do the same in C# ?
You can use an overload of string.Split() that lets you specify the number of parts:
string fileName = "Prefix-Type-hello-j---.xml";
string withoutExtension = Path.GetFileNameWithoutExtension(str);
var parts = str.Split(new[]{'-'}, 3);
string name = parts[2];
If this is always the structure of your string, this would work :
string name = str.Split(new[]{'-'})[2];
I'm assuming you only want "hello". If you want the rest of the name you could use the overloaded method as #KingCronus suggested :
string name = str.Split(new[]{'-'}, 3)[2];
You can also create an extension function that works like String.IndexOf but that gets the position of the nth occurrence of the specified character:
public static int IndexOfWithCount(this string input, char character, int occurenceNumber)
{
int count = 0;
for (int numCaracter = 0; numCaracter < input.Length; numCaracter++)
if (input[numCaracter] == character)
{
count++;
if (count == occurenceNumber)
return numCaracter;
}
return -1;
}
To use it:
string nameWithoutExt = Path.GetFileNameWithoutExtension(str);
string result = nameWithoutExt.Substring(nameWithoutExt.IndexOfWithCount('-', 2) + 1);
It depends how you define efficient but if lines of code fits your defintion how about:
var str = "prefix-Type-hello-j---.xml";
var name = Regex.Match(str, #"(.+?-)(.+?-)(?<name>.+)\.").Groups["name"].Value;
The first two capturing groups consume the prefix and the the type then the namedgroup consumes the name until the extension starts.
This code assumes there is always a match, if there is not it will throw a nullreference exception.
See Regex.Match

Get the different substrings from one main string

I have the following main string which contains link Name and link URL. The name and url is combined with #;. I want to get the string of each link (name and url i.e. My web#?http://www.google.com), see example below
string teststring = "My web#;http://www.google.com My Web2#;http://www.bing.se Handbooks#;http://www.books.se/";
and I want to get three different strings using any string function:
My web#?http://www.google.com
My Web2#?http://www.bing.se
Handbooks#?http://www.books.de
So this looks like you want to split on the space after a #;, instead of splitting at #; itself. C# provides arbitrary length lookbehinds, which makes that quite easy. In fact, you should probably do the replacement of #; with #? first:
string teststring = "My web#;http://www.google.com My Web2#;http://www.bing.se Handbooks#;http://www.books.se/";
teststring = Regex.Replace(teststring, #"#;", "#?");
string[] substrings = Regex.Split(teststring, #"(?<=#\?\S*)\s+");
That's it:
foreach(var s in substrings)
Console.WriteLine(s);
Output:
My web#?http://www.google.com
My Web2#?http://www.bing.se
Handbooks#?http://www.books.se/
If you are worried that your input might already contain other #? that you don't want to split on, you can of course do the splitting first (using #; in the pattern) and then loop over substrings and do the replacement call inside the loop.
If these are constant strings, you can just use String.Substring. This will require you to count letters, which is a nuisance, in order to provide the right parameters, but it will work.
string string1 = teststring.Substring(0, 26).Replace(";","?");
If they aren't, things get complicated. You could almost do a split with " " as the delimiter, except that your site name has a space. Do any of the substrings in your data have constant features, such as domain endings (i.e. first .com, then .de, etc.) or something like that?
If you have any control on the input format, you may want to change it to be easy to parse, for example by using another separator between items, other than space.
If this format can't be changed, why not just implement the split in code? It's not as short as using a RegEx, but it might be actually easier for a reader to understand since the logic is straight forward.
This will almost definitely will be faster and cheaper in terms of memory usage.
An example for code that solves this would be:
static void Main(string[] args)
{
var testString = "My web#;http://www.google.com My Web2#;http://www.bing.se Handbooks#;http://www.books.se/";
foreach(var x in SplitAndFormatUrls(testString))
{
Console.WriteLine(x);
}
}
private static IEnumerable<string> SplitAndFormatUrls(string input)
{
var length = input.Length;
var last = 0;
var seenSeparator = false;
var previousChar = ' ';
for (var index = 0; index < length; index++)
{
var currentChar = input[index];
if ((currentChar == ' ' || index == length - 1) && seenSeparator)
{
var currentUrl = input.Substring(last, index - last);
yield return currentUrl.Replace("#;", "#?");
last = index + 1;
seenSeparator = false;
previousChar = ' ';
continue;
}
if (currentChar == ';' && previousChar == '#')
{
seenSeparator = true;
}
previousChar = currentChar;
}
}

Categories

Resources