Matching a Pattern in C# - c#

This may seem like an obvious thing, but I want to know know if there are wildcards in C# for matching strings.
I want to write a program so that users can search for something based on the date value in the database. What I have so far works as long as the whole date is entered, but entering half a date causes a parsing error
I'm using this code now
" ... where Date like " DateTime.Parse(textbox.text.trim()) " + %;"
I want to see if there is an way to see what the user input (only the year, or month and year without day, or for it not to crash if only half the year is entered)
A solution that involves doing this in SQL itself doesn't matter

Solution 1:
public static class MyStringExtensions
{
public static bool Like(this string toSearch, string toFind)
{
return new Regex(#"\A" + new Regex(#"\.|\$|\^|\{|\[|\(|\||\)|\*|\+|\?|\\").Replace(toFind, ch => #"\" + ch).Replace('_', '.').Replace("%", ".*") + #"\z", RegexOptions.Singleline).IsMatch(toSearch);
}
}
examples:
bool willBeTrue = "abcdefg".Like("abcd_fg");
bool willAlsoBeTrue = "abcdefg".Like("ab%f%");
bool willBeFalse = "abcdefghi".Like("abcd_fg");
Solution2:
madate.ToString().Contain("myvalue")
madate.ToString().StartWith("myvalue")
madate.ToString().EndWith("myvalue")
or use sql :
where CONVERT(VARCHAR(24),yourdate, 103) like '%yourvalue%'

where CONVERT(VARCHAR(24),yourdate, 103) like '%yourvalue%'

Related

Get substring in between two other strings?

I am trying to get a string in between two sub strings, but I am running into an issue.
I am trying to use Selenium to automate a web test, and extract the profile ID from the javascript in the page source. I am running into an ArgumentOutOfRangeException?
It doesn't matter with I'm searching for the correct or wrong values and passing them to GetInbetween, it throws this exception. I cannot see anything wrong with my code, so here I am.
Code:
var source = GetSource();
var username = "username1";
Console.WriteLine("Here: " + source.GetInbetween("window.__additionalDataLoaded('/" + username + "/',{\"logging_page_id\":\"", "\","));
Source (truncated for readability):
window.__additionalDataLoaded('/username1/',{"logging_page_id":"profilePage_10216","logging_page_username": "username1"})
Exception:
ArgumentOutOfRangeException
Length cannot be less than zero. (Parameter 'length')
It throws the exception in this method
public static string GetInbetween(this string s, string start, string end)
{
return s[(s.IndexOf(start) + start.Length)..s.IndexOf(end)];
}
LinqPad test:
void Main()
{
var source = "window.__additionalDataLoaded('/username1/',{\"logging_page_id\":\"profilePage_10216\",\"logging_page_username\":\"username1\"})";
var username = "username1";
Console.WriteLine(source.IndexOf("window.__additionalDataLoaded('/" + username + "/',{\"logging_page_id\":\""));
Console.WriteLine(source.IndexOf("\","));
Console.WriteLine($"[{source}]");
Console.WriteLine($"[{"window.__additionalDataLoaded('/" + username + "/',{\"logging_page_id\":\""}]");
Console.WriteLine("Here: " + source.GetInbetween("window.__additionalDataLoaded('/" + username + "/',{\"logging_page_id\":\"", "\"."));
}
You might get this error if end exists in s before start. So try using s.LastIndexOf(end).
It says 'Length cannot be less than zero.' which means IndexOf is returning -1, which it does if the substring is not found in the search string... So you are looking for a substring which doesn't exist in the string. Make sure you have case-sensitivity correct, or use an IndexOf overload which ignores case.
Edit -- Your GetSource() method must not be returning the string you think it is returning... See, works fine explicitly searching that string:
Passing a start index to IndexOf(end) like this seems to fix it.
return s[(s.IndexOf(start) + start.Length)..s.IndexOf(end, s.IndexOf(start))];
The final method looks like this:
public static string GetInbetween(this string s, string start, string end)
{
return s[(s.IndexOf(start) + start.Length)..s.IndexOf(end, s.IndexOf(start))];
}

C# How to loop through an array and find array with most recent date?

New C# learner here. I've scanned through many questions that have already been posted here; I'm sorry if I missed a question like this that has already been asked.
Background
A program I use produces Excel files which have names that contain the date in which they are created. Thousands of Excel files are produced which need to be sorted. My mission here is to extract information from these file names so I am able to move the file to its appropriate location upon confirmation. I am working with a program that successfully finds all associated files with a particular string. I have stored the names of these files within an array.
Example File Name: IMPORTANT_NAME_LISTED (TEXT) [xx-xx-xx] [HH_MM].xlsx
What Is Known
The date is stored within "[ ]" in month/day/year format and it 100 % consistent (meaning that every file will produce the same format, size and location of the date).
I have been trying to develop a solution which targets "." before the file extension and extract the date, but I am struggling.
My Strategy
I have an initial decision, making sure the array that has all of the file names stored contains values.
//code that extracts file names exists above
//file names which interest me are stored within "fileNameArray"
//Determine if the array that collected file names contains values
if (fileNameArray.Length > 1)
{
for (int k = 0; k <= fileNameArray.Length; k++)
{
//Extract date from "[xx-xx-xx] [HH-MM]"
//Transform MM/DD/YY to YY/MM/DD and temporarily store
//Compare each date value that exist within the string
//Target the most recent file - find the array index
//(Ex: 20180831 - today's date)
}
}
My problems stem from properly parsing these individual array items while retaining the array index.
Do any of you recommend a method to use?
LINQ?
Array.FindAll functionality?
I greatly appreciate the help.
-Chris
Edit: Further Information about my situation...
I have a directory of Excel files, which can be in excess of ~1-3k files. I have a program which reads the file names of all of Excel files. A lot of the heavy filtering/sorting takes place before the code I have above which I want to implement.
I have been struggling with solving the issue with respect to handling files with the same name. For example:
I have 4 files that contain the same partial name "DILITHIUM_CRYSTYAL_FUEL_TIME"
My program must be able to filter/search file names through the core name "DILITHIUM_CRYSTYAL_FUEL_TIME". If I have more than one file with the same name, I need to be able to parse the file names in a way which isolates the time stamp within the file name and finds the most recent file.
My files will always show the time stamp, to the left of the file extension, in a 100% consistent manner.
I need to be able to extract this time stamp, and run comparisons against the other files, and isolate the file which is most up-to-date.
LINQ is a good choice for this, combined with Regex for parsing.
var dateRE = new Regex(#"\[(\d\d-\d\d-\d\d)\] \[(\d\d-\d\d)\](?=.xlsx)", RegexOptions.Compiled);
if (fileNameArray.Length > 0) {
var ans = fileNameArray.Select((n, i) => {
var dtMatch = dateRE.Match(n);
return new { Filename = n, Index = i, Filedate = DateTime.ParseExact(dtMatch.Groups[1].Value+" "+dtMatch.Groups[2].Value, "MM-dd-yy HH-mm", CultureInfo.InvariantCulture) };
})
.OrderByDescending(nid => nid.Filedate)
.First();
}
If you want to process the filenames differently, you can replace First() with some other LINQ operation.
I would also go for regex, string parsing and linq:
Working example here: https://dotnetfiddle.net/veUq2N
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;
public class Program
{
private static Random random = new Random();
private static Regex fileNameFragmentPattern = new Regex(#"\[(.*?)\]\.xlsx");
private const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
public static void Main()
{
var fileNames = new List<string>();
// Generate random file names
for (var i = 0; i < 10000; i++) {
fileNames.Add(RandomString(random.Next(8,10)) + "_" + RandomString(random.Next(4,5)) + "_" + "(TEXT) [" + RandomDate().ToString("MM-dd-yyyy") + "].xlsx");
}
// sort files by parsed dates
var dateSortedFileNames = fileNames.OrderByDescending( f => ExtractDate(f));
foreach (var fileName in dateSortedFileNames) {
// you can do anything with sorted files here (or anywhere else below :)
Console.WriteLine(fileName);
}
}
public static DateTime ExtractDate(string fileName) {
var fragment = fileNameFragmentPattern.Match(fileName).Value;
var month = int.Parse(fragment.Substring(1,2));
var day = int.Parse(fragment.Substring(4,2));
var year = int.Parse(fragment.Substring(7,4));
return new DateTime(year, month, day);
}
public static string RandomString(int length)
{
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
public static DateTime RandomDate(int min = -9999, int max = 9999)
{
return DateTime.Now.AddDays(random.Next(min,max));
}
}
Here's a non-regex solution.
var files = new List<string>
{
"IMPORTANT_NAME_LISTED (TEXT) [05-26-92].xlsx",
"IMPORTANT_NAME_LISTED (TEXT) [11-02-89].xlsx",
"IMPORTANT_NAME_LISTED (TEXT) [02-21-96].xlsx"
};
foreach (var fileName in files)
{
var nameOnly = Path.GetFileNameWithoutExtension(fileName);
var dateStr = nameOnly.Substring(nameOnly.Length - 9, 8);
if (DateTime.TryParseExact(dateStr, "MM-dd-yy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime date))
Console.WriteLine(date.ToShortDateString());
}
Since you mention that the 'date' part of the file name is 100% consistent, and we know that the length of your 'date' will always be 8. So using that knowledge,
nameOnly.Substring(nameOnly.Length - 9, 8);
will extract the string starting right after the first [, and will extract 8 characters ending before ].
And if you're 100% positive that the file extension will always be .xlsx, then you can shorten the code even more.
foreach (var fileName in files)
{
var dateStr = fileName.Substring(fileName.Length - 14, 8);
if (DateTime.TryParseExact(dateStr, "MM-dd-yy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime date))
Console.WriteLine(date.ToShortDateString());
}
I wanted to post here again, with what I used to solve my programming problem. It has been a busy past week or so, I apologize for the delay.
Here is a snippet from my code that solved my problem beautifully:
string scanToolDateFinalStgT1 = "";
DateTime scanToolDateFinalT1 = new DateTime(2000, 1, 1, 1, 1, 00);
for (int k = 0; k < scanToolT1Pass.Count(); k++)
{
string scanToolPassNameOnly = Path.GetFileNameWithoutExtension(scanToolT1Pass[k].ToString());
string scanToolDateStr = scanToolPassNameOnly.Substring(scanToolPassNameOnly.IndexOf("[") + 1, 8);
string scanToolTimeStr = scanToolPassNameOnly.Substring(scanToolPassNameOnly.LastIndexOf("[") + 1, 5);
DateTime currentScanToolDate = DateTime.ParseExact(scanToolDateStr + " " + scanToolTimeStr, "MM-dd-yy HH_mm", null);
if (currentScanToolDate > scanToolDateFinalT1)
{
scanToolDateFinalT1 = currentScanToolDate;
scanToolDateFinalStgT1 = scanToolT1Pass[k].ToString();
}
}
Information:
This snippet is aimed at targeting '[xx-xx-xx] [xx-xx].', which is a partial unique identifier for a file name.
The program is passing in 'scanToolT1Pass', which is an array of file names. My task is to take this array and parse the file names, finding the most recent one.
'DateTime scanToolDateFinalT1' has a generic date of 1/01/200, 1:01:00, which is strictly used as a base comparison point. I am certain my data will never require dates which happen before the year 2000. I tried to have a reference date reading all zeros to compare to, but VisualStudio did not approve of that.
Explanation:
Are there more advanced and/or proper methods to parse this data? I'm sure there is. But, for a beginner programmer, this method makes a lot or sense to me and I aim to perfect it in the future. It was most important to me to have a program that functions first, than to invest a lot of study into polishing it.
I was able to implement similar for loops throughout my program, filtering through large amounts of data at a very rapid pace.
Thanks again to the community and to #Sach & #It Man, whose responses I was able to craft into my solution.
Chris
Simpler alternative :
var regex = new Regex(".*\[(.*)-(.*)] \[(.*)].*");
string latest = fileNameArray.OrderBy(s => regex.Replace(s, "$2$1$3")).Last();
Demo and explanation of the pattern can be seen on https://regex101.com/r/Ldh0sa
public struct DatedExcelOutput
{
public string FullName { get; }
public string Name { get; }
public DateTime CreationDate { get; }
public DatedExcelOutput(string fileName)
{
FullName = fileName;
Name = getName();
CreationDate = parseDate();
}
}
It could be called like this:
IEnumerable<string> fileNames = GetFiles();
var datedFiles = fileNames.Select(f => new DatedExcelOutput(f))
.OrderBy(d => d.CreationDate);
You'll likely end up needing to sort these ascending/descending in a UI right? So I don't think it makes sense to throw the date information away.
Edit: Removed unnecessary IO calls as NetMage pointed out.

On searching documents in mongodb collection using ISODate range, I get the error "String was not recognized as a valid DateTime"

I am trying to query MongoDB using the following -
List<BsonDocument> list = NoSqlBusinessEntityBase.LoadByWhereClause("peoplecounting",
string.Concat("{siteid:\"", siteid, "\", locationid:\"", location._id ,"\",
StartTime: {$gte:ISODate(\"",old.ToString("yyyy-mm-dd hh:mm:ss"),"\")},
EndTime: {$lte:ISODate(\"",current.ToString("yyyy-MM-dd hh:mm:ss"), "\"\")}}"));
The LoadByWhereClause() function is as follows -
public static List<BsonDocument> LoadDataByWhere(string table, string whereClause)
{
var collection = db.GetCollection(table);
QueryDocument whereDoc = new QueryDocument(BsonDocument.Parse(whereClause));
var resultSet = collection.Find(whereDoc);
List<BsonDocument> docs = resultSet.ToList();
if (resultSet.Count() > 0)
{
foreach (BsonDocument doc in docs)
{
doc.Set("_id", doc.GetElement("_id").ToString().Split('=')[1]);
}
return docs;
}
else
{
return null;
}
}
Even though the query runs fine in MongoDB console and returns documents
db.peoplecounting.find({siteid:"53f62abf66455068373665ff", locationid:"53f62bb86645506837366603",
StartTime:{$gte:ISODate("2012-12-03 02:40:00")}, EndTime:{$lte:ISODate("2013-12-03 07:40:00")}}
I get the error when I try to load in C# using the LoadByWhereClause function. The error is String was not recognized as a valid DateTime. while parsing the whereClause.
How can I possibly fix this? I am unable to determine what is going wrong here.
It's not entirely clear, but I suspect the problem may well be how you're formatting the date. This:
old.ToString("yyyy-mm-dd hh:mm:ss")
should almost certainly be this:
old.ToString("yyyy-MM-dd HH:mm:ss")
or possibly
old.ToString("yyyy-MM-dd'T'HH:mm:ss")
Because:
mm means minutes. You don't want the minutes value between your year and day-of-month; you want the month (MM)
hh means "hour of half-day" (i.e. 1-12). You want the hour of full day, 0-23 (HH)
ISO-8601 uses a T literal to separate the date frmo the time.
I note that your current.ToString is better, but not correct - it gets the month right, but not the hour. The fact that these are inconsistent is a problem to start with - I would advise you to write a separate method to convert a DateTime appropriately.

Sprache parser with custom fields

I have a report server that needs to parse a string with some arguments controlling what is in the report.
I am using the parser library sprache to help with this. All is working fine except for one thing I'm stuck on.
I have a time filter that can be one of the following values: today, yesterday, last week, last month, none or custom.
It is custom that is giving me some grief. All of the others are just simple strings. Custom also has a from and to properties afterwards.
private static readonly Parser<DataFilterEntity> TimeFilter =
from filter in Parse.String("today").Return(DataFilterEntity.Today)
.Or(Parse.String("yesterday").Return(DataFilterEntity.Yesterday)
.Or(Parse.String("last week").Return(DataFilterEntity.LastWeek)
.Or(Parse.String("last month").Return(DataFilterEntity.LastMonth)
.Or(Parse.String("none").Return(DataFilterEntity.None))
.Or(Parse.String("custom").Return(DataFilterEntity.Custom())))))
select filter;
The custom line is the problem. I need to parse the "custom" string but then parse the from and to DateTime fields as well and pass them through to DataFilterEntity.Custom(from, to)
Any ideas much appreciated.
You need to create a parser for DateTime first and then a parser for your custom type. Here's a 'simplest thing that could possibly work' example. You'd probably want to make the DateTimeParser a bit more specific in the values it accepts. I don't know what the constructor for your DataFilterEntity looks like, so I guessed :)
public static readonly Parser<DateTime> DateTimeParser =
from day in Parse.Number
from s1 in Parse.Char('/')
from month in Parse.Number
from s2 in Parse.Char('/')
from year in Parse.Number
select new DateTime(int.Parse(year), int.Parse(month), int.Parse(day));
public static readonly Parser<DataFilterEntity> CustomParser =
from a1 in Parse.String("custom").Token()
from fromDateTime in DateTimeParser.Token()
from toDateTime in DateTimeParser.Token()
select new DataFilterEntity(fromDateTime.ToShortDateString() + " -> " + toDateTime.ToShortDateString());
public static readonly Parser<DataFilterEntity> TimeFilter =
Parse.String("today").Return(DataFilterEntity.Today)
.Or(Parse.String("yesterday").Return(DataFilterEntity.Yesterday)
.Or(Parse.String("last week").Return(DataFilterEntity.LastWeek)
.Or(Parse.String("last month").Return(DataFilterEntity.LastMonth)
.Or(Parse.String("none").Return(DataFilterEntity.None))
.Or(CustomParser))));
public void TestIt()
{
var result = TimeFilter.Parse("custom 21/3/2013 10/4/2013");
Console.Out.WriteLine("result.Value = {0}", result.Value);
}

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