I have been around in circles with this one and need some help. I have a method that evaluates code, so if I pass this Eval("DateTime.Now.Year - 1986") it returns 29, its working great and this means I can have inline code in my posts that dynamically evaluate at runtime (this might present some security concerns, but that for some other time), here's the example string I am trying to work with: string inStr = "this year is [EVAL]DateTime.Now.Year[/EVAL] and it has been [EVAL]DateTime.Now.Year - 1986[/EVAL] years since 1986"; I need a regex that will replace all [EVAL] instances and return the full text with the evaluated results. Anyone?
You want a Regex, you can have a regex...
string inStr = "this year is [EVAL]DateTime.Now.Year[/EVAL] and it has been [EVAL]DateTime.Now.Year - 1986[/EVAL] years since 1986";
var rx = new Regex(#"(\[EVAL\])(.*?)(\[/EVAL])");
string outStr = rx.Replace(inStr, RegexReplacer);
with
public static string RegexReplacer(Match match)
{
return Eval(match.Groups[2].Value);
}
or depending on the return type of Eval:
public static string RegexReplacer(Match match)
{
object obj = Eval(match.Groups[2].Value);
return obj != null ? obj.ToString() : string.Empty;
}
The capture group #2 is the (.*?). Note the use of the lazy quantifier .*?, because otherwise the capture would be [EVAL]DateTime.Now.Year[/EVAL] and it has been [EVAL]DateTime.Now.Year - 1986[/EVAL]
Related
This question already has answers here:
Is there an easy way to return a string repeated X number of times?
(21 answers)
Closed 2 years ago.
Is there any way to interpolate variable several times without repeating?
For example:
var name = "bla";
Console.WriteLine($"foo {name:repeat:2} bar")
to print
foo blabla bar
I'm particularly interested in interpolating several line breaks instead of repeating {Environment.NewLine} several times in the interpolation mask like this:
$"{Environment.NewLine}{Environment.NewLine}"
public static string Repeat(this string s, int times, string separator = "")
{
return string.Join(separator, Enumerable.Repeat(s, times));
}
Then use:
Console.WriteLine($"foo {name.Repeat(2)} bar")
You could write an extension method for the string type, thats repeating its input. Then simply use this method within the curly braces.
You could also use
var name = "bla";
Console.WriteLine("foo {0}{0} bar", name);
// or
var s = String.Format("foo {0}{0} bar", name);
It will help you not repeating the same string, just index of it.
More about String Format
I'm accepting an input string that I want to be a ternary statement that works on strings. So my method signature would look like this:
public string Parse(string value, string ternaryStatement)
and there parameters would give these results:
Parse(null, "=?It's Null:It's Not Null") == "It's Null" // Empty string would
Parse("", "=?It's Null:It's Not Null") == "It's Null" // be same as null
This example is fairly simple, Split the string first by '?' then by ':'
But of course I need a method to handle escape characters, "\", "\?" and ":", where "\" is valid anywhere, "\?" would only be valid before the first unescaped "?" and ":" would only be valid after that same "?".
Parse(#"\?\", #"=\\\?\\?\:Match\::\:No Match\:") == ":Match:"
Parse(#"\?\", #"!=\\\?\\?\:No Match\::\:Match\:") == ":Match:"
But this is really complicated. I believe I can perform it using regular expressions, but that just creates another problem since this is well beyond my limited understanding of regular expressions. What's the best way to tackle this problem?
Edit 1
Some of the background: I'm storing a format for a URL in a database config table (It's actually Dynamics 365 for Customer Engagement, but that doesn't matter at this point). The format is stored as strings, and the parameters that are required are defined in code. So generally it looks like this:
Format: "https://something.com?Foo={0}&Bar={1}"
Description: "0 - Foo, 1 - Bar"
where the description is used both for the person that is formatting the url, and the developer that needs to know how to structure the format statement.
The problem I'm running into right now is that I have a url that requires at least one of two different parameters. If one of the values is null or empty, it will error if included in the url. So I need a way of saying, if Foo is null or Bar is null, don't include the name or &. Ideally I'd like to implement this like this:
"https://something.com?{0:=?:Foo={{0}}}&}{1:=?:Bar={{1}}}}"
So if Foo is null and Bar is "Bar" the output would be
"https://something.com?Bar=Bar"
I could also see this being used if we need to switch between a 0/1 for a boolean to true/false without having to change code:
"https://something.com?{0:=0?false:true}"
The two regexes should be:
Regex rx = new Regex(#"(?<=(?:^|[^\\])(?:\\\\)*)\?");
Regex rx2 = new Regex(#"(?<=(?:^|[^\\])(?:\\\\)*):");
Use them like:
var m = rx.Match(str);
if (m.Success)
{
int ix = m.Index;
}
The main point of the two rx is that the searched string (\? or :) must be preceded by
(?<=(?:^|[^\\])(?:\\\\)*)
that is the beginning of the string ^ or not a \ ([^\\]) plus zero or an even number of \\ that is (?:\\\\)*.
A all-in-one regex is:
Regex rx = new Regex(#"^(?<operator>=|!=|<=|>=|<|>)(?<cmp>(?:(?:\\.)|[^?:])*)\?(?<true>(?:(?:\\.)|[^?:])*):(?<false>(?:(?:\\.)|[^?:])*)$");
if (m.Success)
{
string op = m.Groups["operator"].Value;
string cmp = m.Groups["cmp"].Value;
string true1 = m.Groups["true"].Value;
string false1 = m.Groups["false"].Value;
}
In op you'll get the comparison operator used, in cmp the comparand, in true1 and false1 the true and false strings. If !m.Success then the string isn't correctly formatted. Comprehending the regex is left as a simple exercise for the reader (unless you comprehend a regex, you shouldn't ever use it, because before or later you'll have to modify it/fix it/debug it)
Solution to returning different values based on input string
Why do you need to pass in a ternary statement / wouldn't this make more sense?
string Parse(string value, string returnIfNull, string returnIfNotNull)
{
return string.IsNullOrEmpty(value) ? returnIfNull: returnIfNotNull;
}
Console.WriteLine(Parse("", "treat as null", "not expected"));
Console.WriteLine(Parse("hello", "not expected", "this value's not null"));
Parsing a ternary string for values
However, if you really need to do this, you could use something like the below:
private static readonly Regex TernaryParserRegex = new Regex(
#"^=\?(?<ifNull>(?:\\(\\\\)*:|[^:])*)(?<!\\):(?<ifNotNull>(?:\\(\\\\)*:|[^:])*)$"
/* , RegexOptions.Compiled //include this line for performance if appropriate */
);
private string[] ParseTernaryString (string ternaryStatement)
{
var results = TernaryParserRegex.Match(ternaryStatement);
if (results.Success)
{
string[] returnVal = {
results.Groups["ifNull"].Value
,
results.Groups["ifNotNull"].Value
};
return returnVal;
}
else
{
throw new Exception("Invalid Ternary Statement"); //use an appropriate exception type here; or have the function return `{null,null}` / some other default as appropriate
}
}
public string Parse(string value, string ternaryStatement)
{
var returnValues = ParseTernaryString(ternaryStatement);
return string.IsNullOrEmpty(value) ? returnValues[0]: returnValues[1];
}
//Example Usage:
Console.WriteLine(Parse("", "=?treat as null:not expected"));
Console.WriteLine(Parse("hello", "=?not expected:this value's not null"));
An explanation of the regex & additional examples are available here:
https://regex101.com/r/YJ9qd3/1
Appending non-null/blank values to a URL's Query String
public void Main()
{
var url = "https://example.com?something=keepMe&foo=FooWasHere&bar=swapMeQuick";
var dict = new System.Collections.Generic.Dictionary<string, string>();
dict.Add("foo", null);
dict.Add("bar", "hello");
dict.Add("boo", "new");
Console.WriteLine(CreateUri(url, dict).ToString());
}
Uri CreateUri(string uri, IDictionary<string, string> parameters)
{
return CreateUri(new Uri(uri), parameters);
}
Uri CreateUri(Uri uri, IDictionary<string, string> parameters)
{
var query = HttpUtility.ParseQueryString(uri.Query); //https://msdn.microsoft.com/en-us/library/ms150046(v=vs.110).aspx; though returns HttpValueCollection
foreach (string key in parameters.Keys)
{
if (string.IsNullOrEmpty(parameters[key]))
{ //parameter is null or empty; if such a parameter already exists on our URL, remove it
query.Remove(key); //if this parameter does not already exist, has no effect (but no exception is thrown)
}
else
{ //parameter has a value; add or update the query string with this value
query.Set(key, parameters[key]);
}
}
var builder = new UriBuilder(uri);
builder.Query = query.ToString();
return builder.Uri;
}
I want to create a function but I don't know how it would work or how to create it. I want to create a function something similar to below.
I have a string, lets say its this for example:
string myString = "This is my string and it will forever be my string.";
What if I wanted to split this by a space and get each part? which I do...
string[] parts = myString.Split(' ');
Now, I want to get everything but the first 3 words in my string parts, how would I merge each string in parts except the first 3? which will return
string and it will forever be my string.
Something similar to this:
public string getMergedString(string[] parts, int start) // This method would return everything from where you told it to start...
{
}
public string getMergedString(string[] parts, int start) // This method would return everything from where you told it to start...
{
return String.Join(" ", parts.Skip(start));
}
Quick explanation of the code:
string.Join(separator, IEnumerable values)
This joins an IEnumerable object containing strings to one, unified string.
Docs: https://msdn.microsoft.com/en-us/library/system.string.join(v=vs.110).aspx
parts.Skip(int count)
This part of the code skips a given amount of elements, before returning them.
Skip(int count) is an extension method found in the System.Linq namespace.
You need .Net 3.5 or higher in order for you to be able to use this method.
Docs: https://msdn.microsoft.com/en-us/library/bb358985(v=vs.110).aspx
string myString = "This is my string and it will forever be my string.";
string[] words = myString.Split(' ');
var myNewString = string.Join(" ", words.Skip(3));
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].
I am really a beginner, I already know
string.indexOf("");
Can search for a whole word, but when I tried to search for e.g: ig out of pig, it doesn't work.
I have a similar string here(part of):
<Special!>The moon is crashing to the Earth!</Special!>
Because I have a lot of these in my file and I just cannot edited all of them and add a space like:
< Special! > The moon is crashing to the Earth! </ Special! >
I need to get the sub-string of Special! and The moon is crashing to the Earth!
What is the simple way to search for a part of a word without adding plugins like HTMLAgilityPack?
IndexOf will work, you are probably just using it improperly.
If your string is in a variable call mystring you would say mystring.IndexOf and then pass in the string you are looking for.
string mystring = "somestring";
int position = mystring.IndexOf("st");
How are you using it? You should use like this:
string test = "pig";
int result = test.IndexOf("ig");
// result = 1
If you want to make it case insensitive use
string test = "PIG";
int result = test.IndexOf("ig", StringComparison.InvariantCultureIgnoreCase);
// result = 1
String.IndexOf Method - MSDN
Please try this:
string s = "<Special!>The moon is crashing to the Earth!</Special!>";
int whereMyStringStarts = s.IndexOf("moon is crashing");
IndexOf should work with spaces too, but maybe you have new line or tab characters, not spaces?
Sometimes case-sensitivity is important. You may control it by additional parameter called comparisonType. Example:
int whereMyStringStarts = s.IndexOf("Special", StringComparison.OrdinalIgnoreCase);
More information about IndexOf: String.IndexOf Method at MSDN
Anyway, I think you may need regular expressions to create better parser. IndexOf is very primitive method and you may stuck in big mess of code.
string page = "<Special!>The moon is crashing to the Earth!</Special!>";
if (page.Contains("</Special!>"))
{
pos = page.IndexOf("<Special!>");
propertyAddress = page.Substring(10, page.IndexOf("</Special!>")-11);
//i used 10 because <special!> is 10 chars, i used 11 because </special!> is 11
}
This will give you "the moon is crashing to the earth!"