Separating numbers from other signs in a string - c#

I got a string that contains:
"(" ")" "&&" "||"
and numbers (0 to 99999).
I want to get a string and return a list like this:
get:
"(54&&1)||15"
return new List<string>(){
"(",
"54",
"&&",
"1",
")",
"||",
"15"}

I suspect a regex would do the trick here. Something like:
string text = "(54&&1)||15";
Regex pattern = new Regex(#"\(|\)|&&|\|\||\d+");
Match match = pattern.Match(text);
while (match.Success)
{
Console.WriteLine(match.Value);
match = match.NextMatch();
}
The tricky bit in the above is that a lot of stuff needs escaping. The | is the alternation operator, so this is "open bracket or close bracket or && or || or at least one digit".

If you want to extract only numbers from your string you can use the regex
but if you want to parse this string and made some as formula and calculate result you should look at the math expression parser
for example look at this Math Parser

Here's the LINQ/Lambda way to do it:
var operators = new [] { "(", ")", "&&", "||", };
Func<string, IEnumerable<string>> operatorSplit = t =>
{
Func<string, string, IEnumerable<string>> inner = null;
inner = (p, x) =>
{
if (x.Length == 0)
{
return new [] { p, };
}
else
{
var op = operators.FirstOrDefault(o => x.StartsWith(o));
if (op != null)
{
return (new [] { p, op }).Concat(inner("", x.Substring(op.Length)));
}
else
{
return inner(p + x.Substring(0, 1), x.Substring(1));
}
}
};
return inner("", t).Where(x => !String.IsNullOrEmpty(x));
};
Now you just call this:
var list = operatorSplit("(54&&1)||15").ToList();
Enjoy!

Related

C# Split text into Substrings

What I'm actually trying is to split a StreamReader.ReadLine() object such as "1 A & B 2 C & D" into "1", "A & B", "2" and "C & D" substrings. Anybody an idea of a simple algorithm to implement this splitting?
Something like this (using a tiny bit of Linq): ?
static private List<string> Parse(string s)
{
var result = new List<string>();
string[] rawTextParts = s.Split(new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' });
var textParts = rawTextParts.Where(t => !string.IsNullOrWhiteSpace(t)).Select(t => t.Trim());
foreach (string textPart in textParts)
{
string numberstring = s.Substring(0, s.IndexOf(textPart)).Trim();
s = s.Substring(s.IndexOf(textPart) + textPart.Length);
result.Add(numberstring);
result.Add(textPart);
}
return result;
}
Regex is made for pattern matching. There are two patterns, Alphabetic character(s) a non character and alphabetic character(s) or the final pattern of numbers. Here is the regex to do such:
var input = "1 A & B 2 C & D";
var pattern = #"[a-zA-Z]\s+\W\s+[a-zA-Z]|\d+";
var resultItems =
Regex.Matches(input, pattern)
.OfType<Match>()
.Select(m => m.Value)
.ToList();
Result is
The \s+ was not mentioned for that handles all spaces, such it is 1 to many spaces for something like (A & B). If you believe there will be no spaces such A&B use \s* which is zero to many spaces.
It's hard to infer precise requirements from your question. But according to your example I'd come with something like:
void Main()
{
var input = "1 A & B 2 C & D";
var result = Parse(input);
Console.WriteLine(String.Join("\n", result));
}
static IEnumerable<string> Parse(string input)
{
var words = input.Split();
var builder = new StringBuilder();
foreach (var word in words)
{
if (int.TryParse(word, out var value))
{
if (builder.Length > 0)
{
yield return builder.ToString();
builder.Clear();
}
yield return word;
}
else
{
if (builder.Length > 0)
{
builder.Append(' ');
}
builder.Append(word);
}
}
if (builder.Length > 0) // leftovers
{
yield return builder.ToString();
}
}
The output of the above code will be:
1
A & B
2
C & D

How to add a space before and after conditional operator in a string?

I have a string e.g "number1<=number2&&number3>number4||number2=number4" and having operator list as -
var operators = new List<string> {"=", "!", "<", ">", ">=", "<=", "!=","||","&&"};
So, expectation is to introduce a single space before and after each operator in the string.
"number1 <= number2 && number3 > number4 || number2 = number4"
I tried following code but its not working for instance, e.g, <, >=
public static string AddSpaceBeforeAndAfterOperator(string expression) {
var operators = new List<string> {"=", "!", "<", ">", ">=", "<=", "!=", "||", "&&"};
foreach (var op in operators) {
var index = expression.IndexOf(op, StringComparison.Ordinal);
if (index >= 0) {
if (expression.Substring(index - 1) != " ") {
expression = expression.Insert(index-1, " ");
}
expression = expression.Insert(index + op.Length + 1, " ");
}
}
return expression;
}
Note: The Operator list is coming as random.
Any help would be appreciated!
I would recommend a regex solution.
First, you need to escape all your operators, join them together with |:
var operatorsString = string.Join("|",
operators.OrderByDescending(x => x.Length).Select(Regex.Escape).ToArray()
);
// OrderByDescending here because we want the longer operators to be matched first.
Next, create the regex:
var regex = $"\\s*({operatorString})\\s*";
Using the operators array in the question, the array looks like this:
\s*(<=|>=|!=|==|\|\||&&|=|!|<|>)\s?*
Note that \s* is used to check if the operator is already surrounded by spaces. If it is, those spaces will be matched and replaced.
The replacement is:
$1
Code:
Regex.Replace(input, regex, " $1 ");
Note the leading and trailing space.
Also note that Regex is inside the System.Text.RegularExpressions namespace.
Demo
Im not sure if this is the most effective way to do this, but is is rally simple by using replace
public static string AddSpaceBeforeAndAfterOperator(string expression)
{
var operators = new List<string> { "=", "!", "<", ">", ">=", "<=", "!=", "||", "&&" };
foreach (var op in operators)
{
expression = expression.Replace(op, " " + op + " ");
}
return expression;
}

Remove Alphabetic characters from a string, leaving numbers and symbols

I have a bunch of strings that I'm trying to parse the date out of. I have a script that will parse the date, but it's having trouble with all the extra letters in the string. I need to remove all the letters but leave characters such as - / _
I'm not particularly good with Regex, so all attempts to do this so far have ended with too many characters getting removed.
Here's a few sample strings to help:
Littleton, CO - Go-Live 5/8
Brunswick - Go-Live 5/14
CutSheeet_Go Live-5-14-14
Go Live - 5-19-2014
You could do this:
Regex.Replace(input, "([a-zA-Z,_ ]+|(?<=[a-zA-Z ])[/-])", "");
Working regex example:
http://regex101.com/r/kD2jF4
From your example data, output would be:
5/8
5/14
5-14-14
5-19-2014
You can use a function like this:
public static string Parse(string source)
{
var numbers = new [] {'0','1','2','3','4','5','6','7','8','9' };
var chars = new [] { '-', '/', '_' };
return new string(source
.Where(x => numbers.Contains(x) || chars.Contains(x))
.ToArray()).Trim(chars);
}
Here is fiddle
Try this:
public static string StripCrap(string input)
{
return input.Where(c => char.IsNumber(c) || c == '_' || c == '/' ||
c == '-').Aggregate("", (current, c) => current + c);
}
Or, if you want a maintainable list:
public static string StripCrap(string input)
{
char[] nonCrapChars = {'/', '-', '_'};
return input.Where(c => char.IsNumber(c) || nonCrapChars.Contains(c)).Aggregate("", (current, c) => current + c);
}
Or...You could also create an extension method:
public static string ToNonCrappyString(this string input)
{
char[] nonCrapChars = {'/', '-', '_'};
return input.Where(c => char.IsNumber(c) || nonCrapChars.Contains(c)).Aggregate("", (current, c) => current + c);
}
and you can call it like this:
string myString = "Hello 1234!";
string nonCrappyString = myString.ToNonCrappyString();
use this pattern .*?(\d+[\d-\/]*\d+)|.* and replace with $1 Demo

How to find out if regexp parsed string part contains another string?

Say we have a list of strings L, a given string S. We have a regexp like (\w+)\-(\w+) we want to get all L elements for which S matches $1 of regexp. How to do such thing?
You can do this:
// sample data
string[] L = new string[] { "bar foo", "foo bar-zoo", "bar-", "zoo bar-foo" };
string S = "bar";
Regex regex = new Regex(#"(\w+)\-(\w+)");
string[] res = L.Where(l => {
Match m = regex.Match(l);
if (m.Success) return m.Groups[1].Value == S;
else return false;
}).ToArray();
and get
foo bar-zoo
zoo bar-foo
An easier way that probably works out for you too is to include S in the regex:
Regex regex = new Regex(S + #"\-(\w+)");
string[] res = L.Where(l => regex.Match(l).Success).ToArray();

Implementing method in a functional style

I have the following method:
private List<string> CreateSegments(string virtualPath)
{
List<string> segments = new List<string>();
int i = virtualPath.IndexOf('/', 1);
while (i >= 0 && i < virtualPath.Length)
{
var segment = virtualPath.Substring(0, i);
if (!string.IsNullOrWhiteSpace(segment))
{
segments.Add(segment);
segments.Add(VirtualPathUtility.Combine(segment, "default"));
}
i = virtualPath.IndexOf('/', i + 1);
}
segments.Add(virtualPath);
segments.Add(VirtualPathUtility.Combine(virtualPath, "default"));
return segments;
}
Basically, it creates path segments which I will use to check if a file exists in any of those segments. Like this:
string[] extensions = GetRegisteredExtensions();
HttpServerUtilityBase server = HttpContext.Current.Server;
List<string> segments = CreateSegments(virtualPath);
// check if a file exists with any of the registered extensions
var match = extensions.SelectMany(x => segments.Select(s => string.Format("{0}.{1}", s, x)))
.FirstOrDefault(p => System.IO.File.Exists(server.MapPath(p)));
All the above code looks like it could use some clean up and optimization, but I'm looking for a way to use LINQ if possible to generate the segments.
Something like: var segments = virtualPath.Split('/').SelectMany(...) and get a result similar to the following:
/path
/path/default
/path/to
/path/to/default
/path/to/file
/path/to/file/default
Where virtualPath would contain the value "/path/to/file"
EDIT: Changed string.Format("{0}/{1}", ...) to VirtualPathUtility.Combine(..., ...)
Any ideas?
One way would be to incrementally select the path segments, then "join" it with an empty string and "/default" to get the two variations:
string path = #"/path/to/file";
string temp = "";
var query = path.Split('/')
.Where(s => !string.IsNullOrEmpty(s))
.Select((p) => {temp += ("/" + p); return temp;} )
.SelectMany(s => new[]{"","/default"}.Select (d => s + d) );
If you first define an extension method like this:
public static IEnumerable<int> SplitIndexes(this string subject, char search)
{
for(var i = 1; i < subject.Length; i++)
{
if(subject[i] == search)
{
yield return i;
}
}
yield return subject.Length;
}
Then you could do this:
var endings = new string[] { string.Empty, "/default" };
var virtualPath = "/path/to/file";
var results =
from i in virtualPath.SplitIndexes('/')
from e in endings
select virtualPath.Substring(0, i) + e;
Or if you prefer query syntax:
var endings = new string[] { string.Empty, "/default" };
var virtualPath = "/path/to/file";
var results = virtualPath.SplitIndexes('/')
.SelectMany(i => endings.Select(e => virtualPath.Substring(0, i) + e));
The result will be:
/path
/path/default
/path/to
/path/to/default
/path/to/file
/path/to/file/default
As others have suggested, you can this in a more platform independent way by using Path.Combine, like this:
var endings = new string[] { string.Empty, "default" }; // Note: no / before default
var results =
from i in virtualPath.SplitIndexes(Path.DirectorySeparatorChar)
from e in endings
select Path.Combine(virtualPath.Substring(0, i), e);
This might do the trick. It is not the most succinct code but it seems very readable to me. It uses string concatenation because for short strings, like paths or URLs, it is faster than any of the alternatives.
Edit: fixed and tested.
var query = path.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries)
.Aggregate(new List<string>(), (memo, segment) => {
memo.Add(memo.DefaultIfEmpty("").Last() + "/" + segment);
return memo;
}).Aggregate(new List<string>(), (memo, p) => {
memo.Add(p);
memo.Add(p + "/default");
return memo;
});
The higher-order function you're looking for is called scan. There is no such function in normal LINQ, but you can find it in MoreLinq. Using that, your code could look like this:
private List<string> CreateSegments(string virtualPath)
{
return virtualPath.Split('/')
.Scan((s1, s2) => s1 + '/' + s2)
.Skip(1)
.SelectMany(p => new[] { p, p + "/default" })
.ToList();
}
This assumes your path will be always an absolute path starting with a /. For relative paths, you will need to remove the .Skip(1) part.
If you don't want to get MoreLinq just for this one method, you can just copy its source into your project.
The provided answers so far are more succinct, but this is what I came up with:
public static IEnumerable<string> PossiblePaths(string basePath)
{
return PossiblePaths(basePath.Split(new[] { "/" },
StringSplitOptions.RemoveEmptyEntries));
}
private static IEnumerable<string> PossiblePaths(IEnumerable<string> segments,
string current = "/")
{
if (segments.Count() == 0)
{
return new string[0];
}
else
{
string next = current + segments.First();
return new[] { next, next + "/default" }
.Concat(PossiblePaths(segments.Skip(1), next + "/"));
}
}
Something like this:
public static IEnumerable<string> EnumerateSegments( this IEnumerable<string> segments )
{
StringBuilder sb = new StringBuilder() ;
foreach ( string segment in segements )
{
sb.Append( Path.DirectorySeparatorChar ).Append( segment ) ;
yield return sb.ToString() ;
int n = sb.Length ;
sb.Append( Path.DirectorySeparatorChar ).Append("default") ;
yield return sb.ToString() ;
sb.Length = n ;
}
}
ought to do you.

Categories

Resources