I have a set of strings that contain within them one or more question marks delimited by a comma, a comma plus one or more spaces, or potentially both. So these strings are all possible:
BOB AND ?
BOB AND ?,?,?,?,?
BOB AND ?, ?, ? ,?
BOB AND ?,? , ?,?
?, ? ,? AND BOB
I need to replace the question marks with #P#, so that the above samples would become:
BOB AND #P1
BOB AND #P1,#P2,#P3,#P4,#P5
BOB AND #P1,#P2,#P3,#P4
BOB AND #P1,#P2,#P3,#P4
#P1,#P2,#P3 AND BOB
What's the best way to do this without regex or Linq?
I ignored the trimming of spaces in your output example, because if this is to be used in a SQL statement, the spaces are irrelevent. This should perform pretty well due to the use of StringBuilder rather than repeated calls to Replace, Substring, or other string methods.:
public static string GetParameterizedString(string s)
{
var sb = new StringBuilder();
var sArray = s.Split('?');
for (var i = 0; i < sArray.Length - 1; i++)
{
sb.Append(sArray[i]);
sb.Append("#P");
sb.Append(i + 1);
}
sb.Append(sArray[sArray.Length - 1]);
return sb.ToString();
}
If you don't want regex or LINQ, I would just write a loop, and use the "ReplaceFirst" method from this question to loop over the string, replacing each occurrence of ? with the appropriate #P#.\
How do I replace the *first instance* of a string in .NET?
Maybe something like this:
int i = 0;
while (myString.Contains("?"))
{
myString = myString.ReplaceFirst("?", "#P" + i);
i++;
}
Note that "ReplaceFirst" is not a standard method on string - you have to implement it (e.g. as an extension method, in this example).
Why not generate your SQL as you get your parameters defining proper CASE in your code and give it to execution at the very end when it is ready?
If you want something out of the box :)
string toFormat = "?, ? ,? AND BOB";
while (toFormat.Contains(" "))
toFormat = toFormat.Replace(" ", " ");
toFormat = toFormat.Replace("?", "{0}");
string formated = string.Format(toFormat, new PCounter());
Where PCounter is like this
class PCounter{
int i = 0;
public override string ToString(){
return "#P" + (++i);
}
}
I think something like the below should do it.
string input = "BOB AND ?,?,?,?,?";
int number = 1;
int index = input.IndexOf("?");
while (index > -1)
{
input = input.Substring(0, index).Trim() + " #P" + number++.ToString() + input.Substring(index + 1).Trim();
index = input.IndexOf("?");
}
Related
I think I am too dumb to solve this problem...
I have some formulas which need to be "translated" from one syntax to another.
Let's say I have a formula that goes like that (it's a simple one, others have many "Ceilings" in it):
string formulaString = "If([Param1] = 0, 1, Ceiling([Param2] / 0.55) * [Param3])";
I need to replace "Ceiling()" with "Ceiling(; 1)" (basically, insert "; 1" before the ")").
My attempt is to split the fomulaString at "Ceiling(" so I am able to iterate through the string array and insert my string at the correct index (counting every "(" and ")" to get the right index)
What I have so far:
//splits correct, but loses "CEILING("
string[] parts = formulaString.Split(new[] { "CEILING(" }, StringSplitOptions.None);
//splits almost correct, "CEILING(" is in another group
string[] parts = Regex.Split(formulaString, #"(CEILING\()");
//splits almost every letter
string[] parts = Regex.Split(formulaString, #"(?=[(CEILING\()])");
When everything is done, I concat the string so I have my complete formula again.
What do I have to set as Regex pattern to achieve this sample? (Or any other method that will help me)
part1 = "If([Param1] = 0, 1, ";
part2 = "Ceiling([Param2] / 0.55) * [Param3])";
//part3 = next "CEILING(" in a longer formula and so on...
As I mention in a comment, you almost got it: (?=Ceiling). This is incomplete for your use case unfortunately.
I need to replace "Ceiling()" with "Ceiling(; 1)" (basically, insert "; 1" before the ")").
Depending on your regex engine (for example JS) this works:
string[] parts = Regex.Split(formulaString, #"(?<=Ceiling\([^)]*(?=\)))");
string modifiedFormula = String.join("; 1", parts);
The regex
(?<=Ceiling\([^)]*(?=\)))
(?<= ) Positive lookbehind
Ceiling\( Search for literal "Ceiling("
[^)] Match any char which is not ")" ..
* .. 0 or more times
(?=\)) Positive lookahead for ")", effectively making us stop before the ")"
This regex is a zero-assertion, therefore nothing is lost and it will cut your strings before the last ")" in every "Ceiling()".
This solution would break whenever you have nested "Ceiling()". Then your only solution would be writing your own parser for the same reasons why you can't parse markup with regex.
Regex.Replace(formulaString, #"(?<=Ceiling\()(.*?)(?=\))","$1; 1");
Note: This will not work for nested "Ceilings", but it does for Ceiling(), It will also not work fir Ceiling(AnotherFunc(x)). For that you need something like:
Regex.Replace(formulaString, #"(?<=Ceiling\()((.*\((?>[^()]+|(?1))*\))*|[^\)]*)(\))","$1; 1$3");
but I could not get that to work with .NET, only in JavaScript.
This is my solution:
private string ConvertCeiling(string formula)
{
int ceilingsCount = formula.CountOccurences("Ceiling(");
int startIndex = 0;
int bracketCounter;
for (int i = 0; i < ceilingsCount; i++)
{
startIndex = formula.IndexOf("Ceiling(", startIndex);
bracketCounter = 0;
for (int j = 0; j < formula.Length; j++)
{
if (j < startIndex) continue;
var c = formula[j];
if (c == '(')
{
bracketCounter++;
}
if (c == ')')
{
bracketCounter--;
if (bracketCounter == 0)
{
// found end
formula = formula.Insert(j, "; 1");
startIndex++;
break;
}
}
}
}
return formula;
}
And CountOccurence:
public static int CountOccurences(this string value, string parameter)
{
int counter = 0;
int startIndex = 0;
int indexOfCeiling;
do
{
indexOfCeiling = value.IndexOf(parameter, startIndex);
if (indexOfCeiling < 0)
{
break;
}
else
{
startIndex = indexOfCeiling + 1;
counter++;
}
} while (true);
return counter;
}
If i have a string containing three 0 values, how would i grab them one by one in order to replace them?
the 0's could be located anywhere in the string.
i don't want to use regex.
example string to parse:
String myString = "hello 0 goodbye 0 clowns are cool 0";
right now i can only find the three 0 values if they are right next to each other. i replace them using stringToParse.Replace("0", "whatever value i want to replace it with");
I want to be able to replace each instance of 0 with a different value...
You can do something like this:
var strings = myString.Split('0');
var replaced = new StringBuilder(strings[0]);
for (var i = 1; i < strings.Length; ++i)
{
replaced.Append("REPLACED " + i.ToString());
replaced.Append(strings[i]);
}
pseudolang :
s = "yes 0 ok 0 and 0"
arr = s.split(" 0")
newstring = arr[0] + replace1 + arr[1] + replace2 + arr[2] + replace3
If you have control of these input strings, then I would use a composite format string instead:
string myString = "hello {0} goodbye {1} clowns are cool {2}";
string replaced = string.Format(myString, "replace0", "replace1", "replace2");
public string ReplaceOne(string full, string match, string replace)
{
int firstMatch = full.indexOf(match);
if(firstMatch < 0)
{
return full;
}
string left;
string right;
if(firstMatch == 0)
left = "";
else
left = full.substring(0,firstMatch);
if(firstMatch + match.length >= full.length)
right = "";
else
right = full.substring(firstMatch+match.length);
return left + replace + right
}
If your match can occur in replace, then you will want to track what index your upto and pass it in to indexOf.
Using LINQ and generic function to decouple replacement logic.
var replace = (index) => {
// put any custom logic here
return (char) index;
};
string input = "hello 0 goodbye 0 clowns are cool 0";
string output = new string(input.Select((c, i) => c == '0' ? replace(i) : c)
.ToArray());
Pros:
Char replacement logic decoupled from the string processing (actually LINQ query)
Cons:
Not the best solution from performance perspectives
I Have a string in the form "123456789".
While displaying it on the screen I want to show it as 123-456-789.
Please let me knwo how to add the "-" for every 3 numbers.
Thanks in Advance.
You can use string.Substring:
s = s.Substring(0, 3) + "-" + s.Substring(3, 3) + "-" + s.Substring(6, 3);
or a regular expression (ideone):
s = Regex.Replace(s, #"\d{3}(?=\d)", "$0-");
I'll go ahead and give the Regex based solution:
string rawNumber = "123456789";
var formattedNumber = Regex.Replace(rawNumber, #"(\d{3}(?!$))", "$1-");
That regex breaks down as follows:
( // Group the whole pattern so we can get its value in the call to Regex.Replace()
\d // This is a digit
{3} // match the previous pattern 3 times
(?!$) // This weird looking thing means "match anywhere EXCEPT the end of the string"
)
The "$1-" replacement string means that whenever a match for the above pattern is found, replace it with the same thing (the $1 part), followed by a -. So in "123456789", it would match 123 and 456, but not 789 because it's at the end of the string. It then replaces them with 123- and 456-, giving the final result 123-456-789.
You can use for loop also if the string length is not fixed to 9 digits as follows
string textnumber = "123456789"; // textnumber = "123456789012346" also it will work
string finaltext = textnumber[0]+ "";
for (int i = 1; i < textnumber.Length; i++)
{
if ((i + 1) % 3 == 0)
{
finaltext = finaltext + textnumber[i] + "-";
}
else
{
finaltext = finaltext + textnumber[i];
}
}
finaltext = finaltext.Remove(finaltext.Length - 1);
My example non-parsed data is
"8$154#3$021308831#7$NAME SURNAME#11$2166220160#10$5383237309#52$05408166#"
I want to parse data that is between $ and # strings.
I want to see result like that;
Between 8$ and # -> My data is 154,
Between 3$ and # -> My data is 021308831,
Between 7$ and # -> My data is NAME SURNAME,
Between 11$ and # -> My data is 2166220160,
Between 10$ and # -> My data is 5383237309,
Between 52$ and # -> My data is 05408166.
Thanks for your reply.
(\d+\$)(.*?)#
See it on Rubular
You will find the first part (e.g. 8$) in the capturing group 1 and the according data in the group 2.
The brackets are responsible, that the result is sotred in those capturing groups. The \d+ will match at least one digit. The .*? is a lazy match for everything till the next #.
class Program
{
static void Main(string[] args)
{
string text = "8$154#3$021308831#7$NAME SURNAME#11$2166220160#10$5383237309#52$05408166#";
string[] values = text.Split('$', '#');
for (var i = 0; i < values.Length - 1; i = i + 2)
{
Console.WriteLine("Between " + values[i] + "$ and # -> My data is " + values[i+1]);
}
Console.ReadLine();
}
}
You can split into array based on #.
With
String[] entries = data.Split('#');
you will get an arrays with "8$154", "3$021308831", etc.
Now you just work with the entries and split each one at the dollar sign:
String[] tmp = entries[0].Split('$');
So you get
tmp[0] = "8";
tmp[1] = "154";
Build in some checks and you will be happy. No need for regex here I suppose.
If you have "8$15$4#3$021308831" then you will get in tmp:
tmp[0] = "8"; // your key!
tmp[1] = "15"; // data part
tmp[2] = "4"; // data part ($ is missing!)
So you would have to concat all tmp above index 1:
StringBuilder value = new StringBuilder();
for(int i = 1; i < tmp.Length; i++)
{
if(i > 1) value.Append("$");
value.Append(tmp[i]);
}
Ok, taking stema's expression, which works.
using System.Text.RegularExpressions;
string nonParsed = "8$...";
MatchCollection matches = Regex.Matches(nonparsed, #"(\d+\$)(.*?)#");
StringBuilder result = new StringBuilder();
for(int i = 0; i < matches.Count; i++)
{
Match match = matches[i];
result.AppendFormat("Between {0} and #-> My data is {1}")
match.Groups[1].Value,
match.Groups[2].Value);
if (i < matches.Count - 1)
{
result.AppendLine(",");
}
else
{
result.Append(".");
}
}
return result.ToString();
Thanks to stema, this copes with the $ repeating within the value.
If you want to use regex this should do it.
\$([\w\d\s]+)\#
This will match betweel $ and #:
\$(.*?)#
Is there a way to get the string between.. lets say quote "
The problem with using Indexof and substring is that it gets the first " and last " but not the pair. Like
"Hello" "WHY ARE" "WWWWWEEEEEE"
It will get
Hello" "WHY ARE" "WWWWWEEEEEE
I want it to get to array > Hello, WHY ARE, WWWWEEEEEE
Is there any way to do this?
Something like this?
StringCollection resultList = new StringCollection();
try
{
Regex regexObj = new Regex("\"([^\"]+)\"");
Match matchResult = regexObj.Match(subjectString);
while (matchResult.Success)
{
resultList.Add(matchResult.Groups[1].Value);
matchResult = matchResult.NextMatch();
}
}
catch (ArgumentException ex)
{
// Syntax error in the regular expression
}
If subjectString was "Hello" "WHY ARE" "WWWWWEEEEEE", that should give you a list containing:
Hello
WHY ARE
WWWWWEEEEEE
A more compact example which uses the static Regex class instead, and just writes the matches to console instead of adding to a collection:
var subject = "\"Hello\" \"WHY ARE\" \"WWWWWEEEEEE\"";
var match = Regex.Match(subject, "\"([^\"]+)\"");
while (match.Success)
{
Console.WriteLine(match.Groups[1].Value);
match = match.NextMatch();
}
string s = '"Hello" "WHY ARE" "WWWWWEEEEEE"'
string[] words = s.Split('"');
// words is now ["Hello", " ", "WHY ARE", " ", "WWWWWEEEEEE"]
If you don't want the empty strings, you can split by '" "', in which case you will get ['"Hello', "WHY ARE", 'WWWWWEEEEEE"'].
On the other hand, using regular expressions could be the best solution for what you want. I'm not a C# expert, so I can't give the code from the top of my head, but this is the regex you'll want to use: "(.*?)"
You can also use the String.IndexOf(char value, int startIndex) method which has, as its parameter says, a start index from which the scan is started.
int start = 0;
do
{
int i1 = s.IndexOf('=', start);
if (i1 < 0) break;
int i2 = s.IndexOf('=', i1 + 1);
if (i2 < 0) break;
yield return s.Substring(i1, i2 - i1);
start = i2 + 1;
}
while (start < s.Length);
string s = '"Hello" "WHY ARE" "WWWWWEEEEEE"
s.replace("\" \"", "!*!"); // custom seperator
s.Replace('"', string.empty);
string[] words = s.Split('!*!');
Should do the trick,
Kindness,
Dan