Removing words with special characters in them - c#

I have a long string composed of a number of different words.
I want to go through all of them, and if the word contains a special character or number (except '-'), or starts with a Capital letter, I want to delete it (the whole word not just that character). For all intents and purposes 'foreign' letters can count as special characters.
The obvious solution is to run a loop through each word (after splitting it) and then a loop through each character - but I'm hoping there's a faster way of doing it? Perhaps using Regex but I've almost no experience with it.
Thanks
ADDED:
(What I want for example:)
Input: "this Is an Example of 5 words in an input like-so from example.com"
Output: {this,an,of,words,in,an,input,like-so,from}
(What I've tried so far)
List<string> response = new List<string>();
string[] splitString = text.Split(' ');
foreach (string s in splitString)
{
bool add = true;
foreach (char c in s.ToCharArray())
{
if (!(c.Equals('-') || (Char.IsLetter(c) && Char.IsLower(c))))
{
add = false;
break;
}
if (add)
{
response.Add(s);
}
}
}
Edit 2:
For me a word should be a number of characters (a..z) seperated by a space. ,/./!/... at the end shouldn't count for the 'special character' condition (which is really mostly just to remove urls or the like)
So:
"I saw a dog. It was black!"
should result in
{saw,a,dog,was,black}

So you want to find all "words" that only contain characters a-z or -, for words that are separated by spaces?
A regex like this will find such words:
(?<!\S)[a-z-]+(?!\S)
To also allow for words that end with single punctuation, you could use:
(?<!\S)[a-z-]+(?=[,.!?:;]?(?!\S))
Example (ideone):
var re = #"(?<!\S)[a-z-]+(?=[,.!?:;]?(?!\S))";
var str = "this, Is an! Example of 5 words in an input like-so from example.com foo: bar?";
var m = Regex.Matches(str, re);
Console.WriteLine("Matched: ");
foreach (Match i in m)
Console.Write(i + " ");
Notice the punctuation in the string.
Output:
Matched:
this an of words in an input like-so from foo bar

How about this?
(?<=^|\s+)(?[a-z-]+)(?=$|\s+)
Edit: Meant (?<=^|\s+)(?<word>[a-z\-]+)(?=(?:\.|,|!|\.\.\.)?(?:$|\s+))
Rules:
Word can only be preceded by start of line or some number of whitespace characters
Word can only be followed by end of line or some number of whitespace characters (Edit supports words ending with periods, commas, exclamation points, and ellipses)
Word can only contain lower case (latin) letters and dashes
The named group containing each word is "word"

Have a look at Microsoft's How to: Search Strings Using Regular Expressions (C# Programming Guide) - it's about regexes in C#.

List<string> strings = new List<string>() {"asdf", "sdf-sd", "sdfsdf"};
for (int i = strings.Count-1; i > 0; i--)
{
if (strings[i].Contains("-"))
{
strings.Remove(strings[i]);
}
}

This could be a starting point. right now it just checks only for "." as a special char. This outputs : "this an of words in an like-so from"
string pattern = #"[A-Z]\w+|\w*[0-9]+\w*|\w*[\.]+\w*";
string line = "this Is an Example of 5 words in an in3put like-so from example.com";
System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex(pattern);
line = r.Replace(line,"");

You can do this in two ways, the white-list way and the black-list way. With a white-list you define the set of characters that you consider to be acceptable and with the black-list its the opposite.
Lets assume the white-list way and that you accept only characters a-z, A-Z and the - character. Additionally you have the rule that the first character of a word cannot be an upper case character.
With this you can do something like this:
string target = "This is a white-list example: (Foo, bar1)";
var matches = Regex.Matches(target, #"(?:\b)(?<Word>[a-z]{1}[a-zA-Z\-]*)(?:\b)");
string[] words = matches.Cast<Match>().Select(m => m.Value).ToArray();
Console.WriteLine(string.Join(", ", words));
Outputs:
// is, a, white-list, example

You can use look-aheads and look-behinds to do this. Here's a regex that matches your example:
(?<=\s|^)[a-z-]+(?=\s|$)
The explanation is: match one or more alphabetic characters (lowercase only, plus hyphen), as long as what comes before the characters is whitespace (or the start of the string), and as long as what comes after is whitespace or the end of the string.
All you need to do now is plug that into System.Text.RegularExpressions.Regex.Matches(input, regexString) to get your list of words.
Reference: http://www.mikesdotnetting.com/Article/46/CSharp-Regular-Expressions-Cheat-Sheet

Related

Separate title string with no spaces into words

I want to find and separate words in a title that has no spaces.
Before:
ThisIsAnExampleTitleHELLO-WORLD2019T.E.S.T.(Test)"Test"'Test'[Test]
After:
This Is An Example Title HELLO-WORLD 2019 T.E.S.T. (Test) [Test] "Test" 'Test'
I'm looking for a regular expression rule that can do the following.
I thought I'd identify each word if it starts with an uppercase letter.
But also preserve all uppercase words as not to space them into A L L U P P E R C A S E.
Additional rules:
Space a letter if it touches a number: Hello2019World Hello 2019 World
Ignore spacing initials that contain periods, hyphens, or underscores T.E.S.T.
Ignore spacing if between brackets, parentheses, or quotes [Test] (Test) "Test" 'Test'
Preserve hyphens Hello-World
C#
https://rextester.com/GAZJS38767
// Title without spaces
string title = "ThisIsAnExampleTitleHELLO-WORLD2019T.E.S.T.(Test)[Test]\"Test\"'Test'";
// Detect where to space words
string[] split = Regex.Split(title, "(?<!^)(?=(?<![.\\-'\"([{])[A-Z][\\d+]?)");
// Trim each word of extra spaces before joining
split = (from e in split
select e.Trim()).ToArray();
// Join into new title
string newtitle = string.Join(" ", split);
// Display
Console.WriteLine(newtitle);
Regular expression
I'm having trouble with spacing before the numbers, brackets, parentheses, and quotes.
https://regex101.com/r/9IIYGX/1
(?<!^)(?=(?<![.\-'"([{])(?<![A-Z])[A-Z][\d+?]?)
(?<!^) // Negative look behind
(?= // Positive look ahead
(?<![.\-'"([{]) // Ignore if starts with punctuation
(?<![A-Z]) // Ignore if starts with double Uppercase letter
[A-Z] // Space after each Uppercase letter
[\d+]? // Space after number
)
Solution
Thanks for all your combined effort in answers. Here's a Regex example. I'm applying this to file names and have exclude special characters \/:*?"<>|.
https://rextester.com/FYEVE73725
https://regex101.com/r/xi8L4z/1
Here is a regex which seems to work well, at least for your sample input:
(?<=[a-z])(?=[A-Z])|(?<=[0-9])(?=[A-Za-z])|(?<=[A-Za-z])(?=[0-9])|(?<=\W)(?=\W)
This patten says to make a split on a boundary of one of the following conditions:
what precedes is a lowercase, and what precedes is an uppercase (or
vice-versa)
what precedes is a digit and what follows is a letter (or
vice-versa)
what precedes and what follows is a non word character
(e.g. quote, parenthesis, etc.)
string title = "ThisIsAnExampleTitleHELLO-WORLD2019T.E.S.T.(Test)[Test]\"Test\"'Test'";
string[] split = Regex.Split(title, "(?<=[a-z])(?=[A-Z])|(?<=[0-9])(?=[A-Za-z])|(?<=[A-Za-z])(?=[0-9])|(?<=\\W)(?=\\W)");
split = (from e in split select e.Trim()).ToArray();
string newtitle = string.Join(" ", split);
This Is An Example Title HELLO-WORLD 2019 T.E.S.T. (Test) [Test] "Test" 'Test'
Note: You might also want to add this assertion to the regex alternation:
(?<=\W)(?=\w)|(?<=\w)(?=\W)
We got away with this here, because this boundary condition never happened. But you might need it with other inputs.
First few parts are similar to #revo answer: (?<!^|[A-Z\p{P}])[A-Z]|(?<=\p{P})\p{P}, additionally I add the following regex to space between number and letter: (?<=[a-z])(?=\d)|(?<=\d)(?=[a-z])|(?<=[A-Z])(?=\d)|(?<=\d)(?=[A-Z]) and to detect OTPIsADevice then replace with lookahead and lookbehind to find uppercase with a lowercase: (((?<!^)[A-Z](?=[a-z]))|((?<=[a-z])[A-Z]))
Note that | is or operator which allowed all the regex to be executed.
Regex: (?<!^|[A-Z\p{P}])[A-Z]|(?<=\p{P})\p{P}|(?<=[a-z])(?=\d)|(?<=\d)(?=[a-z])|(?<=[A-Z])(?=\d)|(?<=\d)(?=[A-Z])|(((?<!^)[A-Z](?=[a-z]))|((?<=[a-z])[A-Z]))
Demo
Update
Improvised a bit:
From: (?<!^|[A-Z\p{P}])[A-Z]|(?<=\p{P})\p{P}|(?<=[a-z])(?=\d)|(?<=\d)(?=[a-z])|(?<=[A-Z])(?=\d)|(?<=\d)(?=[A-Z])
into: (?<!^|[A-Z\p{P}])[A-Z]|(?<=\p{P})\p{P}|(?<=\p{L})\d which do the same thing.
(((?<!^)(?<!\p{P})[A-Z](?=[a-z]))|((?<=[a-z])[A-Z]))|(?<!^)(?=[[({&])|(?<=[)\]}!&}]) improvised from OP comment which is adding exception to some punctuation: (((?<!^)(?<!['([{])[A-Z](?=[a-z]))|((?<=[a-z])[A-Z]))|(?<!^)(?=[[({&])|(?<=[)\\]}!&}])
Final regex:
(?<!^|[A-Z\p{P}])[A-Z]|(?<=\p{P})\p{P}|(?<=\p{L})\d|(((?<!^)(?<!\p{P})[A-Z](?=[a-z]))|((?<=[a-z])[A-Z]))|(?<!^)(?=[[({&])|(?<=[)\]}!&}])
Demo
Aiming for simplicity rather than huge regex, I would recommend this code with small simple patterns (comments with explanation are in code):
string str = "ThisIsAnExampleTitleHELLO-WORLD2019T.E.S.T.(Test)\"Test\"'Test'[Test]";
// insert space when there is small letter followed by upercase letter
str = Regex.Replace(str, "(?<=[a-z])(?=[A-Z])", " ");
// insert space whenever there's digit followed by a ltter
str = Regex.Replace(str, #"(?<=\d)(?=[A-Za-z])", " ");
// insert space when there's letter followed by digit
str = Regex.Replace(str, #"(?<=[A-Za-z])(?=\d)", " ");
// insert space when there's one of characters ("'[ followed by letter or digit
str = Regex.Replace(str, #"(?=[(\[""'][a-zA-Z0-9])", " ");
// insert space when what preceeds is on of characters ])"'
str = Regex.Replace(str, #"(?<=[)\]""'])", " ");
You could reduce the requirements to shorten the steps of a regular expression using a different interpretation of them. For example, the first requirement would be the same as to say, preserve capital letters if they are not preceded by punctuation marks or capital letters.
The following regex works almost for all of the mentioned requirements and may be extended to include or exclude other situations:
(?<!^|[A-Z\p{P}])[A-Z]|(?<=\p{P})\p{P}
You have to use Replace() method and use $0 as substitution string.
See live demo here
.NET (See it in action):
string input = #"ThisIsAnExample.TitleHELLO-WORLD2019T.E.S.T.(Test)""Test""'Test'[Test]";
Regex regex = new Regex(#"(?<!^|[A-Z\p{P}])[A-Z]|(?<=\p{P})\p{P}", RegexOptions.Multiline);
Console.WriteLine(regex.Replace(input, #" $0"));

Checking syntax of strings - C#

I'm trying to find out how to analyze the syntax of a sentence in C#.
In my case I have a syntax which every sentence has to follow.
The syntax looks like this:
A 'B' is a 'C'.
Every sentence has to contain five words. The first word of my sentence has to be 'A', the third 'is' and the fourth 'a'.
Now I would like to examine a test sentence if it matches my syntax.
Test sentence:
A Dog is no Cat.
In this example the test sentence would be wrong, because the fourth word is 'no' and not 'a' what it should be basend on the syntax.
I read about LINQ where I can query sentences that contain a specified set of words.
The code would look something like this:
//Notice the third sentence would have the correct syntax
string text = "A Dog is no Cat. My Dog is a Cat. A Dog is a Cat.";
//Splitting text into single sentences
string[] sentences = text.Split(new char[] { '.'});
//Defining the search terms
string[] wordToMatch ={"A", "is"};
//Find sentences that contain all terms I'm looking for
var sentenceQuery = from sentence in sentences
let w = sentence.Split(new Char[] {'.'})
where w.Distinct().Intersect(wordsToMatch).Count == wordsToMatch.Count()
select sentence;
With this code I could check if the sentences contain my terms I'm looking for, but the problem is it's not checking the position of the words in the sentence.
Is there a way I could check the position as well or maybe a better way to check the syntax of a sentence with C#?
Try using regular expressions, something like this:
using System.Text.RegularExpressions;
...
string source = "A Dog is no Cat.";
bool result = Regex.IsMatch(source, #"^A\s+[A-Za-z0-9]+\s+is\s+a\s+[A-Za-z0-9]+\.$");
Pattern explanation:
^ - start of the string (anchor)
A - Letter A
\s+ - one or more whitelines (spaces)
[A-Za-z0-9]+ - 1st word (can contain A..Z, a..z letters and 0..9 digits)
\s+ - one or more whitelines (spaces)
is - is
\s+ - one or more whitelines (spaces)
a - a
\s+ - one or more whitelines (spaces)
[A-Za-z0-9]+ - 2nd word (can contain A..Z, a..z letters and 0..9 digits)
\. - full stop
$ - end of the string (anchor)
You can slightly modify the code and obtain actual 1st and 2nd strings' values:
string source = "A Dog is a Cat."; // valid string
string pattern =
#"^A\s+(?<First>[A-Za-z0-9]+)\s+is\s+a\s+(?<Second>[A-Za-z0-9]+)\.$";
var match = Regex.Match(source, pattern);
if (match.Success) {
string first = match.Groups["First"].Value; // "Dog"
string second = match.Groups["Second"].Value; // "Cat"
...
}
A regular expression would work for this, and would be the most concise, but may not be the most readable solution. Here is a simple method that will return true if the sentence is valid:
private bool IsSentenceValid(string sentence)
{
// split the sentence into an array of words
char[] splitOn = new char[] {' '};
string[] words = sentence.ToLower().Split(splitOn); // make all chars lowercase for easy comparison
// check for 5 words.
if (words.Length != 5)
return false;
// check for required words
if (words[0] != "a" || words[2] != "is" || words[3] != "a")
return false;
// if we got here, we're fine!
return true;
}
Just want to throw ideas. I would write three classes for this:
SentenceManager: which gets string as a sentence and has a public method public string GetWord(word_index). for example GetWord(3) would return the 3rd word in the sentence that has been given to the class constructor.
SentenceSyntax: in this class, you can say how many words your sentence must have. what words must be known and you can set the index of those words too.
SyntaxChecker: this class gets a SentenceSyntax object and a SentenceManager object and has a function called Check which returns true if the syntax matches the sentence.
remember there can be thousands of ways to make something work. but there are some few ways to do it right.
You should definitely do this using Regex or something similar like Dmitry has answered
Just for kicks, I wanted to do it your way. This is how I would do if I was going nuts :)
//Notice the third sentence would have the correct syntax
string text = "A Dog is no Cat.My Dog is a Cat.A Dog is a Cat.";
//Splitting text into single sentences
string[] sentences = text.Split(new char[] { '.' });
string[] wordsToMatch = { "A", "*", "is", "a", "*" };
var sentenceQuery = from sentence in sentences
let words = sentence.Split(' ')
where words.Length == wordsToMatch.Length &&
wordsToMatch.Zip(words, (f, s) => f == "*" || f == s).All(p => p)
select sentence;
Using this code, you can also get flexibility like cases insensitive comparison, and trim space around the word, etc - of course you will have to code for that

Retrieve Alphabet with white space

I would like to retrieve the alphabet only but the code is not enough to make it.
What am I missing?
[A-Öa-ö]+$
16440 dallas
23941 cityO < You also have white space after "O"
931 00 Texas
10581 New Orleans
It's because you specify a sequence from the ASCII character table. And åäö is not directly after Z in the ascii table.
You can see it here: http://www.asciitable.com/
So what you need is a regex that specifies those separately:
[A-Za-zåäöÅÄÖ]+$
So the complete regex is:
var re = new Regex("([A-Za-zåäöÅÄÖ]+)$", RegexOptions.Multiline);
var matches = re.Matches(data);
Console.WriteLine(matches[0].Groups[1].Value);
However, since you want to allow white spaces within the name (as for "New Orleans") you need to allow it, simply include it in the regex:
var re = new Regex("([A-Za-zåäöÅÄÖ ]+)$", RegexOptions.Multiline);
Unfortunately that also includes white spaces in the beginning and the end:
" New Orleans "
To fix that you start by specifying the regex as greedy, i.e. tell it to use less characters:
new Regex("([A-Za-zåäöÅÄÖ ]+?)$", RegexOptions.Multiline)
The problem with that is that it do not take other lines than New orleans. Don't ask me why. To fix that I told the regex that it must have a space between the digits and the text and that there may be a space after the text:
var re = new Regex("\\s([A-Za-zåäöÅÄÖ ]+?)[\\s]*$", RegexOptions.Multiline);
which works with all lines.
Regex breakdown:
\\s A single whitespace (which should not be included in the match since it's not in the parenthesis expression)
([A-Za-zåäöÅÄÖ ]+?)
Find a character which either is in the alphabet or space
+ there must be one or more
? use greedy search.
[\\s]*
[\\s] Find a white space character
* There must be zero or more if it
Alternative
As an alternative to regex you can do something like this:
public IEnumerable<string> GetCodes(string data)
{
var lines = data.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
foreach (var line in lines)
{
for (var i = 0; i < line.Length; i++)
{
if (!char.IsLetter(line[i]))
continue;
var text = line.Substring(i).TrimEnd(' ');
yield return text;
break;
}
}
}
Which is invoked like:
var codes = GetCodes(yourData).ToList();
In C#, you can use \p{L} Unicode category class to match all Unicode characters. You may match zero or more whitespace characters with \s*. End of string is $ (or \Z or \z). The word you need can be captured and this capture can easily be retrieved from the match result via GroupCollection.
Thus, you can use
(\p{L}+)\s*$
or - if you plan to match specific Finnish, etc. letters:
(?i)([A-ZÅÄÖ]+)\s*$
See the regex demo
C# demo:
var strs = new string[] {"16440 dallas", "23941 cityO ", "931 00 Texas", "10581 New Orleans"};
foreach (var s in strs) {
var match = Regex.Match(s, #"(\p{L}+)\s*$");
if (match.Success)
{
Console.WriteLine(match.Groups[1].Value);
}
}

String split on words and queued punctuation characters

Here is the pattern I use for now:
string pattern = #"^(\s+|\d+|\w+|[^\d\s\w])+$";
Regex regex = new Regex(pattern);
if (regex.IsMatch(inputString))
{
Match match = regex.Match(inputString);
foreach (Capture capture in match.Groups[1].Captures)
{
if (!string.IsNullOrWhiteSpace(capture.Value))
tmpList.Add(capture.Value);
}
}
return tmpList.ToArray<string>();
With this I retrieve an array of strings, on item for each word and one item for each punctuation character.
What I'd like to achieve now is grouping queued punctuation chars in only one item, i.e. for now if there are three dots one after the other, I get three items in my array each containing a dot. Ultimately I'd like to have one item with three dots (or any other punctuation char for that matter).
Try this regex:
^(\s+|\d+|\w+|[^\d\s\w]+)+$
Description
Try with following pattern. I added an extra +. Let me know if you intended something else. Hope it helps.
string pattern = #"^(\s+|\d+|\w+|[^\d\s\w]+)+$";
For inputString = "abc;..cbe;aaa...kjaskjas" I get this result:
abc
;..
cbe
;
aaa
...
kjaskjas

Searching for a RegEx to split a text in it words

I am searching for a RegularExpression to split a text in it words.
I have tested
Regex.Split(text, #"\s+")
But this gives me for example for
this (is a) text. and
this
(is
a)
text
and
But I search for a solution, that gives me only the words - without the (, ), . etc.
It should also split a text like
end.begin
in two words.
Try this:
Regex.Split(text, #"\W+")
\W is the counterpart to \w, which means alpha-numeric.
You're probably better off matching the words rather than splitting.
If you use Split (with \W as Regexident suggested), then you could get an extra string at the beginning and end. For example, the input string (a b) would give you four outputs: "", "a", "b", and another "", because you're using the ( and ) as separators.
What you probably want to do is just match the words. You can do that like this:
Regex.Matches(text, "\\w+").Cast<Match>().Select(match => match.Value)
Then you'll get just the words, and no extra empty strings at the beginning and end.
You can do:
var text = "this (is a) text. and";
// to replace unwanted characters with space
text = System.Text.RegularExpressions.Regex.Replace(text, "[(),.]", " ");
// to split the text with SPACE delimiter
var splitted = text.Split(null as char[], StringSplitOptions.RemoveEmptyEntries);
foreach (var token in splitted)
{
Console.WriteLine(token);
}
See this Demo

Categories