I need to extract data from a text file in ASP .NET.
Example Data:
; comment
data = astringvalue
; comment
; string values
person = bob
animal = rabbit
; boolean values (yes / no)
isValid = yes
isAnimal = no
I will be creating a GUI control for each line that is not a comment.
What is the best way to extract each line and determine if it is a string or a Boolean value.
Performance is a must as the file can be quite large.
Edit: At some point i will have need to update the values with the updated ones from the web page.
private void ShowConfig()
{
string configLine = String.Empty;
using (TextReader tr = File.OpenText(#"textfile"))
{
do
{
configLine = tr.ReadLine();
if (!String.IsNullOrEmpty(configLine) && !configLine.Contains(Convert.ToChar(";")))
{
CreateControl(configLine);
}
} while (configLine != null);
}
private void CreateControl(string configline)
{
string lineHeader = string.Empty;
string lineValue = String.Empty;
for (int i = 0; i < configline.Length; i++)
{
if (configline[i] == Convert.ToChar("="))
{
lineHeader = configline.Remove(i).TrimEnd();
lineValue = configline.Remove(0, ++i).TrimStart();
if (GetValueType(lineValue) is CheckBox)
{
this.Panel1.Controls.Add(CreateCheckBox(lineValue, lineHeader));
}
else
{
this.Panel1.Controls.Add(CreateLabel(lineHeader));
this.Panel1.Controls.Add(CreateTextBox(lineValue, lineHeader));
}
this.Panel1.Controls.Add(CreateNewLine());
break;
}
}
}
private Control GetValueType(string Value)
{
switch (Value)
{
case "yes":
case "no":
return new CheckBox();
default:
return new TextBox();
}
}
In the future i will need to check for more value types than string and boolean.
How about something like this? However, I think that working any kind of serious type-recognition into this schema is likely to be error prone. Why not use a better serialization in the first place? Something that captures type info in the serialized data?
var data=#" ; comment
data = value
; comment
; string values
person = Bob
animal = Rabbit
; boolean values (yes / no)
isValid = yes
isAnimal = no";
var parsed = data
.Split(new[]{"\r\n","\r","\n"}, StringSplitOptions.RemoveEmptyEntries)
.Select(line => line.Trim())
.Where(line => !line.StartsWith(";"))
.Select(line => line.Split('=').Select(item => item.Trim()))
.Where(kv => kv.Count() == 2)
.Select(kv => new{key = kv.First(), value = kv.Last()})
.Select(kv =>
new{kv.key, kv.value, isBool = Regex.IsMatch(kv.value,"yes|no")});
Taking #Rubens' comments on-board, if the data source is too large to load in at once, you could stream the data with the addition of a helper method:
static IEnumerable<string> Lines(string filename)
{
using (var sr = new StreamReader(filename))
{
while (!sr.EndOfStream)
{
yield return sr.ReadLine();
}
}
}
then:
Lines(#"c:\path\to\data")
.Select(line => line.Trim())
.Where(line => !line.StartsWith(";"))
.Select(line => line.Split('=').Select(item => item.Trim()))
.Where(kv => kv.Count() == 2)
.Select(kv => new{key = kv.First(), value = kv.Last()})
.Select(kv =>
new{kv.key, kv.value, isBool = Regex.IsMatch(kv.value,"yes|no")});
StreamReader sr = null;
while(!sr.EndOfStream)
{
string line = sr.ReadLine();
if (string.IsNullOrEmpty(line) || line.StartsWith(";")) continue;
string[] tokens = line.Split("= ".ToCharArray(),
StringSplitOptions.RemoveEmptyEntries);
if(tokens.Length == 2)
{
if("Yes".Equals(tokens[1], StringComparison.CurrentCultureIgnoreCase) ||
"No" .Equals(tokens[1], StringComparison.CurrentCultureIgnoreCase))
{
// boolean
}
else
{
// non boolean
}
}
}
Related
I want to read settings from a text file.
string MySettingsFile = "Settings.txt";
Normally, the file would have 4 lines.
SettingA:Alpha1
SettingB:Bravo2
SettingC:Charlie1
SettingD:Delta6
I want to get each line in it's own variable, like this :
string MyAlpa = "Alpha1";
string MyBravo = "Bravo2";
string MyCharlie = "Charlie1";
string MyDelta = "Delta6";
Normally, I would just read the lines in a loop, reading each line and setting the string as I go.
If, however, Line 4 is missing, and I am looking for the part of the line after the colon, I get an error if I check for it like this...
MyDelta = MyDeltaSubstring(MyDelta.LastIndexOf(':') + 1);
Is there a way to 'check for the existence of a specific line' before I attempt to get the SubString (so it doesn't get an error), like in
a function separate that has try, catch, finally with return either the string I want or the word "Missing", if that line is missing (and then stop since there are no more lines)?
function DoesLineExist(int X, string myFile)
{
string MyString;
try ()
{
// read line X from file myFile
// get everything AFTER the ":" and put it in MyString
// ??? //
}
catch (ArgumentException null)
{
MyString = "Missing";
}
catch (ArgumentException e)
{
MyString = "Missing";
}
finally
{
MyString = ? // whatever we got From Line X (after ":")
}
return MyString; // only if line is missing
}
Is there a better way of doing this?
ReadAllLines or something maybe?
You need to first verify that that line is exist or not and then again check that is line contains key/value pair of settings is exist and then project your key value pair into dictionary and then get each setting to your variable by its key name.
Here i create a console app for your demonstration purpose.
class Program
{
static void Main(string[] args)
{
Dictionary<string, string> dictSettings = new Dictionary<string, string>();
string MyAlpa = "";
string MyBravo = "";
string MyCharlie = "";
string MyDelta = "";
var lines = File.ReadAllLines(#"C:\Users\xxx\source\repos\ConsoleApp4\ConsoleApp4\Files\Sample.txt");
for (var i = 0; i < lines.Length; i += 1)
{
var line = lines[i];
//DoesLineExist(line);
if (!string.IsNullOrEmpty(line) && line.Contains(":"))
{
string settingKey = line.Split(':')[0];
string settingValue = line.Split(':')[1];
dictSettings.Add(settingKey, settingValue);
}
}
MyAlpa = dictSettings.ContainsKey("SettingA") ? dictSettings["SettingA"] : "";
MyBravo = dictSettings.ContainsKey("SettingB") ? dictSettings["SettingB"] : "";
MyCharlie = dictSettings.ContainsKey("SettingC") ? dictSettings["SettingC"] : "";
MyDelta = dictSettings.ContainsKey("SettingD") ? dictSettings["SettingD"] : "";
Console.WriteLine(MyAlpa);
Console.WriteLine(MyBravo);
Console.WriteLine(MyCharlie);
Console.WriteLine(MyDelta);
Console.ReadLine();
}
//private static void DoesLineExist(string line)
//{
// if (!string.IsNullOrEmpty(line) && line.Contains(":"))
// {
// string settingKey = line.Split(':')[0];
// string settingValue = line.Split(':')[1];
// dictSettings.Add(settingKey, settingValue);
// }
//}
}
Input:
SettingA:Alpha1
SettingB:Bravo2
SettingC:Charlie1
Output:
Input:
SettingA:Alpha1
SettingC:Charlie1
SettingD:
Output:
Here is few different way to build the dictionary or list object we will use later. The choice is simple are those key unique or do you have multiple value for some SettingB. If the relation is one-one a Dictionary could be a solution. Giving you access to method like ContainsKey
var regexDictionary = Regex.Matches( File.ReadAllText(path)
, "(?<key>.+):(?<value>.+)")
.OfType<Match>()
.Where(m => m.Success)
.ToDictionary(m => m.Groups["key"].Value.Trim(),
m => m.Groups["value"].Value.Trim());
var ObjList = File.ReadAllLines(path)
.Select(line => line.Split(':'))
.Select(x => new MyObject {
prop1 = x[0],
prop2 = x[1]
// etc
})
var linQDictionary = File.ReadAllLines(path)
.Select(line => line.Split(':'))
.ToDictionary(
c => x[0],
c => x[1]
);
Does the key exist in the dictionary?
if (!dictionary .ContainsKey("SettingB"))
{
Console.WriteLine("For key = \"SettingB\", value = {0}.", dictionary["SettingB"]);
}
In a list of object:
if (ObjList .Any(x=> x.prop1 == "SettingZ" ))
{
// Select the object.
}
In an ASP.Net MVC4 application, I'm using the following code to process a Go To Webinar Attendees report (CSV format).
For some reason, the file that is being loaded is not being released by IIS and it is causing issues when attempting to process another file.
Do you see anything out of the ordinary here?
The CSVHelper (CsvReader) is from https://joshclose.github.io/CsvHelper/
public AttendeesData GetRecords(string filename, string webinarKey)
{
StreamReader sr = new StreamReader(Server.MapPath(filename));
CsvReader csvread = new CsvReader(sr);
csvread.Configuration.HasHeaderRecord = false;
List<AttendeeRecord> record = csvread.GetRecords<AttendeeRecord>().ToList();
record.RemoveRange(0, 7);
AttendeesData attdata = new AttendeesData();
attdata.Attendees = new List<Attendee>();
foreach (var rec in record)
{
Attendee aa = new Attendee();
aa.Webinarkey = webinarKey;
aa.FullName = String.Concat(rec.First_Name, " ", rec.Last_Name);
aa.AttendedWebinar = 0;
aa.Email = rec.Email_Address;
aa.JoinTime = rec.Join_Time.Replace(" CST", "");
aa.LeaveTime = rec.Leave_Time.Replace(" CST", "");
aa.TimeInSession = rec.Time_in_Session.Replace("hour", "hr").Replace("minute", "min");
aa.Makeup = 0;
aa.RegistrantKey = Registrants.Where(x => x.email == rec.Email_Address).FirstOrDefault().registrantKey;
List<string> firstPolls = new List<string>()
{
rec.Poll_1.Trim(), rec.Poll_2.Trim(),rec.Poll_3.Trim(),rec.Poll_4.Trim()
};
int pass1 = firstPolls.Count(x => x != "");
List<string> secondPolls = new List<string>()
{
rec.Poll_5.Trim(), rec.Poll_6.Trim(),rec.Poll_7.Trim(),rec.Poll_8.Trim()
};
int pass2 = secondPolls.Count(x => x != "");
aa.FirstPollCount = pass1;
aa.SecondPollCount = pass2;
if (aa.TimeInSession != "")
{
aa.AttendedWebinar = 1;
}
if (aa.FirstPollCount == 0 || aa.SecondPollCount == 0)
{
aa.AttendedWebinar = 0;
}
attdata.Attendees.Add(aa);
attendeeToDB(aa); // adds to Oracle DB using EF6.
}
// Should I call csvread.Dispose() here?
sr.Close();
return attdata;
}
Yes. You have to dispose objects too.
sr.Close();
csvread.Dispose();
sr.Dispose();
Better strategy to use using keyword.
You should use usings for your streamreaders and writers.
You should follow some naming conventions (Lists contains always multiple entries, rename record to records)
You should use clear names (not aa)
I have a method that runs spellcheck in a word, but what i want here is that if the word already exist in my errorlist it should not be add as error. below is my code.
public void CheckSpelling()
{
int lineno = 0;
bool start = false;
foreach (string line in _contentList)
{
lineno++;
if (line.Contains("<text>"))
{
start = true;
}
if (start)
{
foreach (Match match in Regex.Matches(line, "<.*?>[^<]+</(.*?)>", RegexOptions.IgnoreCase))
{
List<string> customdiclist = new List<string>(File.ReadAllLines(Combine(AppDomain.CurrentDomain.BaseDirectory, ConfigurationManager.AppSettings["customdic"])));
string[] strArray = Regex.Replace(match.Value, "</?[^>]+>", string.Empty).Split(' ');
foreach (string word in strArray)
{
if ((word.Trim() != string.Empty) && ((word.Substring(0, 1) != word.Substring(0, 1).ToUpper()) && !_helper.CheckSpelling(Regex.Match(word, "[a-zA-Z]+").Value) && !customdiclist.Contains(word)))
{
ErrorModel errorModel = new ErrorModel()
{
LineNumber = lineno,
ErrorMessage = "Please Check Misspelled words",
Text = word
};
ErrorList.Add(errorModel);
}
}
}
}
}
}
_helper.CheckSpelling
class Helper
{
private static readonly string DictPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ConfigurationManager.AppSettings["dictionary"]);
private readonly Hunspell _splCheck = new Hunspell(DictPath + #"\nl_NL.aff", DictPath + #"\nl_NL.dic");
public bool CheckSpelling(string strWord)
{
if(!_splCheck.Spell(strWord.Trim()))
{
return false;
}
return true;
}
}
Can some one help, I dont want any duplicate word in my errorlist.
You can use LINQ to check if your ErrorList already contains an ErrorModel object with a Text property which has the value of the word you are checking.
Using FirstOrDefault you can check to see if it returns a null. If an item cannot be found in the list, a null will be returned as the default value because your list is a collection of class objects (whose default value is null).
If the FirstOrDefault returns null then the word has not been added to the ErrorList.
For more information see When to use .First and when to use .FirstOrDefault with LINQ?.
foreach (string word in strArray)
{
if ((word.Trim() != string.Empty) && ((word.Substring(0, 1) != word.Substring(0, 1).ToUpper()) && !_helper.CheckSpelling(Regex.Match(word, "[a-zA-Z]+").Value) && !customdiclist.Contains(word)))
{
if (ErrorList.FirstOrDefault(e => e.Text == word) == null)
{
ErrorModel errorModel = new ErrorModel()
{
LineNumber = lineno,
ErrorMessage = "Please Check Misspelled words",
Text = word
};
ErrorList.Add(errorModel);
}
}
}
I want get out put like 1.2.3.4.5.. but in this code I got values like 1,10,100,101.. plz help me friends
private void btn_load_Click_1(object sender, EventArgs e)
{
{
if (textBox1.Text != "")
{
richTextBox1.Clear();
string tt = #"" + textBox1.Text;
String sdira = #"" + textBox1.Text;
string[] arrays = Directory.GetFiles(sdira, "*", SearchOption.AllDirectories)
.Select(x => Path.GetFileName(x))
.ToArray();// get only file name and extention
foreach (string name in arrays)
{
//StringBuilder sb = new StringBuilder();
richTextBox1.Text += name + "\n";
//i want get out put like 1.2.3.4.5..
//but in this code i got values like 1,10,100,101..
//plz help me friends[first img is my current out put][1]
}
}
}
Sample
If you want sort the strings as numbers instead of lexicographically you have to parse them:
var orderedFiles = Directory.EnumerateFiles(sdira, "*", SearchOption.AllDirectories)
.Select(x => new { file = x, nameNumber = Path.GetFileName(x).TryGetInt32() })
.Where(x => x.nameNumber.HasValue)
.OrderBy(x => x.nameNumber.Value)
.Select(x => x.file); // or x.nameNumber.Value is you want the number
Here's the parse extension method is use in LINQ queries:
public static int? TryGetInt32(this string item, IFormatProvider formatProvider = null, NumberStyles nStyles = NumberStyles.Any)
{
if (formatProvider == null) formatProvider = NumberFormatInfo.CurrentInfo;
int i = 0;
bool success = int.TryParse(item, nStyles, formatProvider, out i);
if (success)
return i;
else
return null;
}
I have a CSV file with a single column containing people names.
I need to take both the user input and the data in the file, strip all non-alphabetical characters, then convert to lower case before comparing with the two.
The file looks like this:
For Example:
Lets say the user enters: Obrien and the csv file contains O'Brien. To compare the two I need to make both lowercase, remove the apostrophe before comparing.
If the name matches, then I will return the name in the file (not the user input).
I am unable to compare the data when there is more then one name or row in the file.
public string MatchedName(string input)
{
string nameMatch = null;
string[] matchList = null;
const string matchFile = #"C:\matchedfile.txt";
using (StreamReader r = new StreamReader(matchFile))
{
string matchContent = "";
while ((matchContent = r.ReadLine()) != null)
{
matchList = matchContent.Split(',');
}
}
foreach (string name in matchList)
{
nameMatch = name;
}
if (String.Equals(RemoveCharTab(input), RemoveCharTab(nameMatch)))
{
return nameMatch;
}
else
{
return input;
}
}
And the following Regex to strip unwanted characters.
public string RemoveCharTab(string input)
{
return Regex.Replace(input.ToLower(), #"[^a-zA-Z]", "");
}
Your code selects values in last row, not the column with names.
Try the following code:
public string MatchedName(string input)
{
const int nameColumnIndex = 0;
const string matchFile = #"C:\matchedfile.txt";
string normalizedInput = RemoveCharTab(input);
string[] names = File.ReadAllLines(matchFile)
.Select(l => l.Split(',')[nameColumnIndex])
.Select(s => s.Trim())
.ToArray();
return names.FirstOrDefault(n => string.Equals(RemoveCharTab(n), normalizedInput)) ?? input;
}
I am unable to compare the data when there is more then one name or row in the file.
Because according to given code you are just splitting last row of text file. If a file contains just one name that will be fine. Otherwise if there are multiple rows then you are just comparing last row only.
You have to split and compare each row in while loop. See following code:
public string MatchedName(string input)
{
string nameMatch = null;
string[] matchList = null;
const string matchFile = #"C:\matchedfile.txt";
using (StreamReader r = new StreamReader(matchFile))
{
string matchContent = "";
while ((matchContent = r.ReadLine()) != null)
{
matchList = matchContent.Split(',');
foreach(string name in matchList)
{
if (String.Equals(RemoveCharTab(input), RemoveCharTab(name)))
{
nameMatch = name;
break;
}
else
{
continue;
}
}
if(string.IsNullOrEmpty(nameMatch) == false)
break;
else
continue;
} //end of While
}
if (string.IsNullOrEmpty(nameMatch) == true)
{
return input;
}
else
{
return nameMatch;
}
return string.Empty; //or something as per your requirements
}
NOTE: if input is comma-separated then you need to split otherwise you can use just string.