C# Detecting a word in user input textbox - c#

Good evening!
I'm trying to achieve some sort of primitive AI. My Hobby Project basically contains two textboxes. One for input and one for output.
So let's say the user feels the urge to tell my AI his Name after a couple hours of flirting. He would type in "I am called 'Jack Black'"
How would I go about storing his name in a variable? (Or storing First Name and Last Name each in one variable)
Is there some sort of placeholder mechanic where I can store the name string after a set of predicted words? (like "I am called")
My guess:
switch (MyMessage)
{
case "Hi":
BotSays("Hello!");
break;
case "I am called " + PLACEHOLDER:
BotSays("Thats a pretty sick name!");
user_firstName = PLACEHOLDER;
break;
default:
BotSays("Sory me no understand");
break;
}
I hope its understandable what my Question is since I probably cant find the right words to get usefull results on Google, Stack Overflow and Co.
Thank you

Hooboy. I'm not sure if you realize just how monumental of a task you've undertaken. My higher-level suggestion would be: Look up 'Eliza' and try writing a port of it in C#. It's a simple Chat-Bot that you could tweak from there if you're interested.
As for your specific question? I think what you're looking for are Regex'es. Take something like this:
string sampleText = "Hello, my name is Inigo Montoya";
Regex namePattern = new Regex(#"Hello, my name is (?<FirstName>\w+) (?<LastName>\w+)$");
Match match = namePattern.Match(sampleText);
string firstName = match.Groups["FirstName"].Value;
string lastName = match.Groups["LastName"].Value;
MessageBox.Show("He said his name was " + firstName + " " + lastName);
... Regexes are a really neat way of parsing strings in a dynamic way. In this case, it'll let you check whether the name matches that Regex - and if it does, it'll fill out the firstName and lastName variables for you.

Here's one way to do this, although I think regular expressions would make it much simpler. I just don't know RegEx very well.
First you could create a list of the "well known prefixes" to someone introducing themselves:
var stringsBeforeIntroduction = new List<string>
{
"I am called ",
"My name is ",
"People call me ",
"You can refer to me as "
};
And a list of known characters that would follow a name:
var charactersAfterName = new char[]
{
' ', ',', '.', '\n', ';', ':'
};
Then, you can search for the index of the first "introduction prefix" you find:
var firstPrefixFound =
stringsBeforeIntroduction.FirstOrDefault(
prefix => MyMessage.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) > -1);
Now, if firstPrefixFound is not null, then we found the prefix. Next we have to grab the name portion, which will be the rest of the string (or until we hit one of our charactersAfterName characters:
if (firstPrefixFound != null)
{
// Calculate the indexes of the start and end of the name
int prefixLength = firstPrefixFound.Length;
int startName = MyMessage.IndexOf(firstPrefixFound,
StringComparison.OrdinalIgnoreCase) + prefixLength;
int endOfName = MyMessage.IndexOfAny(charactersAfterName, startName) - startName;
if (endOfName < 0) endOfName = MyMessage.Length - startName;
// Assign the name that we found
user_firstName = MyMessage.Substring(startName + prefixLength, endOfName);
}
Putting this together in a function might look like the following, which tries to detect a name from an input string. If it finds a name, it returns true and sets the name parameter to the name found, otherwise it returns false and sets the name parameter to the specified defaultValue string:
private static bool TryGetName(string input, string defaultValue, out string name)
{
var stringsBeforeIntroduction = new List<string>
{
"I am called ",
"My name is ",
"People call me ",
"You can call me ",
"You can refer to me as "
};
var charactersAfterName = new char[]
{
' ', ',', '.', '\n', ';', ':'
};
var firstPrefixFound = stringsBeforeIntroduction.FirstOrDefault(prefix =>
input.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) > -1);
if (firstPrefixFound != null)
{
// Calculate the indexes of the start and end of the name
int prefixLength = firstPrefixFound.Length;
int startName = input.IndexOf(firstPrefixFound,
StringComparison.OrdinalIgnoreCase) + prefixLength;
int endOfName = input.IndexOfAny(charactersAfterName, startName) - startName;
if (endOfName < 0) endOfName = input.Length - startName;
// Assign the name that we found
name = input.Substring(startName, endOfName);
}
else
{
name = defaultValue;
return false;
}
return true;
}
And then the usage would look something like:
string name = "stranger";
while (true)
{
Console.Write("Input some text: ");
Console.WriteLine(TryGetName(Console.ReadLine(), name, out name)
? $"I detected your name is: '{name}'"
: $"I did not detect your name that time, {name}.");
Console.WriteLine();
}
And the output looks like:

Related

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].

Validating header names for an SQL column list

I am currently trying to make a user friendly input for people to input SQL headers for a CSV that is created using a temporary table, however I am having issues with validating and changing the names to SQL friendly column headers.
An example input would be as follows:
Name, Ag-e, Gender, Birth Place, Rac+e
Please keep in mind that the input could be ANY word, these are simply an example.
My ideal final output would for the SQL column headers
name age gender birth_place race
however I am having issues checking for invalid characters (which I haven't actually got around to yet.) but my primary issue I am currently having is checking for spaces between words that SHOULD have a space and other spaces at the start of words.
My current output is coming out as(please note that the invalid characters are for testing later.):
Name Ag-e Gender Birth Place Rac+e
Please note that there are double spaces between every one apart from Birth Place which has a single space as it should.
The code I am currently using to achieve this (or not achieve as you can clearly see) is:
columnNamesList = new List<string>(columnNames.Split(splitChar));
columnNamesList[0] = columnNamesList[0].Trim();
columnNamesList[columnNamesList.Count - 1] = columnNamesList[columnNamesList.Count - 1].TrimEnd();
List<string> removalList = new List<string>();
foreach (string i in columnNamesList)
{
if (string.IsNullOrEmpty(i))
{
removalList.Add(i);
}
}
if (removalList.Count < 0)
{
foreach (string i in removalList)
{
columnNamesList.Remove(i);
}
}
for (int i = 0; i < columnNamesList.Count; i++)
{
string s = string.Empty;
string str = columnNamesList[i];
if (Regex.IsMatch(str, #"\w\s\w+", RegexOptions.IgnoreCase))
{
foreach (char c in str)
{
if (Char.IsLetterOrDigit(c) || c == ' ' || c == ',')
s += c;
s = s.Replace(' ', '_');
columnNamesList[i] = s;
}
}
}
string[] columnArray = columnNamesList.ToArray<string>();
columnNames = String.Join(" ", columnArray);
I thought you said that the input is like the first string, comma separated.
Does this not work? All you have to do is remove the unwanted characters (against a blacklist)
var input = "Name, Ag-e, Gender, Birth Place, Rac+e";
var splitInput = input.Split(',')
.Select(i =>
i.Trim()
.ToLower()
.Replace(' ','_'));
var output = string.Join(" ", splitInput.ToArray());

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;
}
}

Splitting a string which contain multiple symbols to get specific values

I cannot believe I am having trouble with this following string
String filter = "name=Default;pattern=%%;start=Last;end=Now";
This is a short and possibly duplicate question, but how would I split this string to get:
string Name = "Default";
string Pattern = "%%" ;
string start = "Last" ;
string end = "Now" ;
Reason why I ask is my deadline is very soon, and this is literally the last thing I must do. I'm Panicking, and I'm stuck on this basic command. I tried:
pattern = filter.Split(new string[] { "pattern=", ";" },
StringSplitOptions.RemoveEmptyEntries)[1]; //Gets the pattern
startDate = filter.Split(new string[] { "start=", ";" },
StringSplitOptions.RemoveEmptyEntries)[1]; //Gets the start date
I happen to get the pattern which I needed, but as soon as I try to split start, I get the value as "Pattern=%%"
What can I do?
Forgot to mention
The list in this string which needs splitting may not be in any particular order . this is a single sample of a string which will be read out of a stringCollection (reading these filters from Properties.Settings.Filters
Using string.Split this is a two stage process.
In the first case split on ; to get an array of keyword and value pairs:
string[] values = filter.Split(';');
Then loop over the resultant list splitting on = to get the keywords and values:
foreach (string value in values)
{
string[] pair = value.Split('=');
string key = pair[0];
string val = pair[1];
}
String filter = "name=Default;pattern=%%;start=Last;end=Now";
string[] temp = filter.Split('=');
string name = temp[1].Split(';')[0];
string pattern = temp[2].Split(';')[0];
string start = temp[3].Split(';')[0];
string end = temp[4].Split(';')[0];
This should do the trick:
string filter = "name=Default;pattern=%%;start=Last;end=Now";
// Make a dictionary.
var lookup = filter
.Split(';')
.Select(keyValuePair => keyValuePair.Split('='))
.ToDictionary(parts => parts[0], parts => parts[1]);
// Get values out of the dictionary.
string name = lookup["name"];
string pattern = lookup["pattern"];
string start = lookup["start"];
string end = lookup["end"];
The start date ends up at the thrird position in the array:
startDate = filter.Split(new string[] { "start=", ";" }, StringSplitOptions.RemoveEmptyEntries)[2];
Instead of splitting the string once for each value, you might want to split it into the separate key-value pairs, then split each pair:
string[] pairs = filter.Split(';');
string[] values = pairs.Select(pair => pair.Split('=')[1]).ToArray();
string name = values[0];
string pattern = values[1];
string start = values[2];
string end = values[3];
(This code of course assumes that the key-value pairs always come in the same order.)
You could also split the string into intersperced array, so that every other item is a key or a value:
string[] values = filter.Split(new string[] { "=", ";" }, StringSplitOptions.None);
string name = values[1];
string pattern = values[3];
string start = values[5];
string end = values[7];
Edit:
To handle key-values in any order, make a lookup from the string, and pick values from it:
ILookup<string, string> values =
filter.Split(';')
.Select(s => s.Split('='))
.ToLookup(p => p[0], p => p[1]);
string name = values["name"].Single();
string pattern = values["pattern"].Single();
string start = values["start"].Single();
string end = values["end"].Single();
You can use SingleOrDefault if you want to support values being missing from the string:
string name = values["name"].SingleOrDefault() ?? "DefaultName";
The lookup also supports duplicate key-value pairs. If there might be duplicates, just loop through the values:
foreach (var string name in values["name"]) {
// do something with the name
}
Well I tried something like this:
var result = "name=Default;pattern=%%;start=Last;end=Now".Split(new char[]{'=',';'});
for(int i=0;i<result.Length; i++)
{
if(i%2 == 0) continue;
Console.WriteLine(result[i]);
}
and the output is:
Default
%%
Last
Now
Is this what you want?
You see, the thing is now that your Split on filter a second time still starts from the beginning of the string, and it matches against ;, so since the string hasn't changed, you still retrieve previous matches (so your index accessor is off by X).
You could break this down into it's problem parts, such that:
var keyValues = filter.Split(';');
var name = keyValues[0].Split('=')[1];
var pattern = keyValues[1].Split('=')[1];
var start = keyValues[2].Split('=')[1];
var end = keyValues[3].Split('=')[1];
Note that the above code is potentially prone to error, and as such should be properly altered.
You can use the following:
String filter = "name=Default;pattern=%%;start=Last;end=Now";
string[] parts = filter.Split(';');
string Name = parts[0].Substring(parts[0].IndexOf('=') + 1);
string Pattern = parts[1].Substring(parts[1].IndexOf('=') + 1);
string start = parts[2].Substring(parts[2].IndexOf('=') + 1);
string end = parts[3].Substring(parts[3].IndexOf('=') + 1);
Use this:
String filter = "name=Default;pattern=%%;start=Last;end=Now";
var parts = filter.Split(';').Select(x => x.Split('='))
.Where(x => x.Length == 2)
.Select(x => new {key = x[0], value=x[1]});
string name = "";
string pattern = "";
string start = "";
string end = "";
foreach(var part in parts)
{
switch(part.key)
{
case "name":
name = part.value;
break;
case "pattern":
pattern = part.value;
break;
case "start":
start = part.value;
break;
case "end":
end = part.value;
break;
}
}
If you don't need the values in named variables, you only need the second line. It returns an enumerable with key/value pairs.
My solution has the added benefits that the order of those key/value pairs in the string is irrelevant and it silently ignores invalid parts instead of crashing.
I found a simple solution on my own too. Most of your answers would have worked if the list would have been in the same order every single time, but it wont be. the format however, will always stay the same. The solution is a simple iteration using a foreach loop, and then checking if it starts with a certain word, namely, the word I am looking for, like Name, Pattern etc.
Probably not the most cpu efficient way of doing it, but it is C# for dummies level. Really brain-fade level.
Here is my beauty.
foreach (string subfilter in filter.Split(';')) //filter.Split is a string [] which can be iterated through
{
if (subfilter.ToUpper().StartsWith("PATTERN"))
{
pattern = subfilter.Split('=')[1];
}
if (subfilter.ToUpper().StartsWith("START"))
{
startDate = subfilter.Split('=')[1];
}
if (subfilter.ToUpper().StartsWith("END"))
{
endDate = subfilter.Split('=')[1];
}
}

C# , Substring How to access last elements of an array/string using substring

I am generating 35 strings which have the names ar15220110910, khwm20110910 and so on.
The string contains the name of the Id (ar152,KHWM), and the date (20110910). I want to extract the Id, date from the string and store it in a textfile called StatSummary.
My code statement is something like this
for( int 1= 0;i< filestoextract.count;1++)
{
// The filestoextract contains 35 strings
string extractname = filestoextract(i).ToString();
statSummary.writeline( extractname.substring(0,5) + "" +
extractname.substring(5,4) + "" + extractname.substring(9,2) + "" +
extractname.substring(11,2));
}
When the station has Id containing 5 letters, then this code executes correctly but when the station Id is KHWM or any other 4 letter name then the insertion is all messed up. I am running this inside a loop. So I have tried keeping the code as dynamic as possible. Could anyone help me to find a way without hardcoding it. For instance accessing the last 8 elements to get the date??? I have searched but am not able to find a way to do that.
For the last 8 digits, it's just:
extractname.Substring(extractname.Length-8)
oh, I'm sorry, and so for your code could be:
int l = extractname.Length;
statSummary.WriteLine(extractname.substring(0,l-8) + "" +
extractname.Substring(l-8,4) + "" + extractname.Substring(l-4,2) + "" +
extractname.Substring(l-2,2));
As your ID length isn't consistent, it would probably be a better option to extract the date (which is always going to be 8 chars) and then treat the remainder as your ID e.g.
UPDATED - more robust by actually calculating the length of the date based on the format. Also validates against the format to make sure you have parsed the data correctly.
var dateFormat = "yyyyMMdd"; // this could be pulled from app.config or some other config source
foreach (var file in filestoextract)
{
var dateStr = file.Substring(file.Length-dateFormat.Length);
if (ValidateDate(dateStr, dateFormat))
{
var id = file.Substring(0, file.Length - (dateFormat.Length+1));
// do something with data
}
else
{
// handle invalid filename
}
}
public bool ValidateDate(stirng date, string date_format)
{
try
{
DateTime.ParseExact(date, date_format, DateTimeFormatInfo.InvariantInfo);
}
catch
{
return false;
}
return true;
}
You could use a Regex :
match = Regex.Match ("khwm20110910","(?<code>.*)(?<date>.{6})" );
Console.WriteLine (match.Groups["code"] );
Console.WriteLine (match.Groups["date"] );
To explain the regex pattern (?<code>.*)(?<date>.{6}) the brackets groups creates a group for each pattern. ?<code> names the group so you can reference it easily.
The date group takes the last six characters of the string. . says take any character and {6} says do that six times.
The code group takes all the remaining characters. * says take as many characters as possible.
for each(string part in stringList)
{
int length = part.Length;
int start = length - 8;
string dateString = part.Substring(start, 8);
}
That should solve the variable length to get the date. The rest of the pull is most likely dependent on a pattern (suggested) or the length of string (when x then the call is 4 in length, etc)
If you ID isn't always the same amount of letters you should seperate the ID and the Date using ',' or somthing then you use this:
for( int 1= 0;i< filestoextract.count;1++)
{
string extractname = filestoextract[i].ToString();
string ID = extractname.substring(0, extractname.IndexOf(','));
string Date = extractname.substring(extractname.IndexOf(','));
Console.WriteLine(ID + Date);
}

Categories

Resources