I have some class with lots of fields;
public class CrowdedHouse
{
public int value1;
public float value2;
public Guid value3;
public string Value4;
// some more fields below
}
My classmust be (de)serialized into simple Windows text file in the following format
NAME1=VALUE1
NAME2=VALUE2
What is the most convinient way to do that in .NET? This is a text file and all the values must be fist converted to string. Let's assume I have already converted all data to strings.
UPDATE One option would be pinvoke WritePrivateProfileString/WritePrivateProfileString
but these are using the required "[Section]" field that I don't need to use.
EDIT: If you have already converted each data value to strings, simply use the method below to serialize it after making a Dictionary of these values:
var dict = new Dictionary<string, string>
{
{ "value1", "value1value" },
{ "value2", "value2value" },
// etc
}
or use dict.Add(string key, string value).
To read the data, simply split each line around the = and store the results as a Dictionary<string, string>:
string[] lines = File.ReadAllLines("file.ext");
var dict = lines.Select(l => l.Split('=')).ToDictionary(a => a[0], a => a[1]);
To convert a dictionary to the file, use:
string[] lines = dict.Select(kvp => kvp.Key + "=" + kvp.Value).ToArray();
File.WriteAllLines(lines);
Note that your NAMEs and VALUEs cannot contain =.
Writing is easy:
// untested
using (var file = System.IO.File.CreateText("data.txt"))
{
foreach(var item in data)
file.WriteLine("{0}={1}", item.Key, item.Value);
}
And for reading it back:
// untested
using (var file = System.IO.File.OpenText("data.txt"))
{
string line;
while ((file.ReadLine()) != null)
{
string[] parts = line.Split('=');
string key = parts[0];
string value = parts[1];
// use it
}
}
But probably the best answer is : Use XML.
Minor improvement of Captain Comic answer:
To enable = in values: (will split only once)
var dict = lines.Select(l => l.Split(new[]{'='},2)).ToDictionary(a => a[0], a => a[1]);
Related
I am parsing a template file which will contain certain keys that I need to map values to. Take a line from the file for example:
Field InspectionStationID 3 {"PVA TePla #WSM#", "sw#data.tool_context.TOOL_SOFTWARE_VERSION#", "#data.context.TOOL_ENTITY#"}
I need to replace the string within the # symbols with values from a dictionary.
So there can be multiple keys from the dictionary. However, not all strings inside the # are in the dictionary so for those, I will have to replace them with empty string.
I cant seem to find a way to do this. And yes I have looked at this solution:
check if string contains dictionary Key -> remove key and add value
For now what I have is this (where I read from the template file line by line and then write to a different file):
string line = string.Empty;
var dict = new Dictionary<string, string>() {
{ "data.tool_context.TOOL_SOFTWARE_VERSION", "sw0.2.002" },
{"data.context.TOOL_ENTITY", "WSM102" }
};
StringBuilder inputText = new StringBuilder();
StreamWriter writeKlarf = new StreamWriter(klarfOutputNameActual);
using (StreamReader sr = new StreamReader(WSMTemplatePath))
{
while((line = sr.ReadLine()) != null)
{
//Console.WriteLine(line);
if (line.Contains("#"))
{
}
else
{
writeKlarf.WriteLine(line)
}
}
}
writeKlarf.Close();
THe idea is that for each line, replace the string within the # and the # with match values from the dictionary if the #string# is inside the dictionary. How can I do this?
Sample Output Given the line above:
Field InspectionStationID 3 {"PVA TePla", "sw0.2.002", "WSM102"}
Here because #WSM# is not the dictionary, it is replaced with empty string
One more thing, this logic only applies to the first qurter of the file. The rest of the file will have other data that will need to be entered via another logic so I am not sure if it makes sense to read the whole file in into memory just for the header section?
Here's a quick example that I wrote for you, hopefully this is what you're asking for.
This will let you have a <string, string> Dictionary, check for the Key inside of a delimiter, and if the text inside of the delimiter matches the Dictionary key, it will replace the text. It won't edit any of the inputted strings that don't have any matches.
If you want to delete the unmatched value instead of leaving it alone, replace the kvp.Value in the line.Replace() with String.Empty
var dict = new Dictionary<string, string>() {
{ "test", "cool test" }
};
string line = "#test# is now replaced.";
foreach (var kvp in dict)
{
string split = line.Split('#')[1];
if (split == kvp.Key)
{
line = line.Replace($"#{split}#", kvp.Value);
}
Console.WriteLine(line);
}
Console.ReadLine();
If you had a list of tuple that were the find and replace, you can read the file, replace each, and then rewrite the file
var frs = new List<(string F, string R)>(){
("#data.tool_context.TOOL_SOFTWARE_VERSION#", "sw0.2.002"),
("#otherfield#", "replacement here")
};
var i = File.ReadAllText("path");
frs.ForEach(fr => i = i.Replace(fr.F,fr.R));
File.WriteAllText("path2", i);
The choice to use a list vs dictionary is fairly arbitrary; List has a ForEach method but it could just as easily be a foreach loop on a dictionary. I included the ## in the find string because I got the impression the output is not supposed to contain ##..
This version leaves alone any template parameters that aren't available
You can try matching #...# keys with a help of regular expressions:
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
...
static string MyReplace(string value, IDictionary<string, string> subs) => Regex
.Replace(value, "#[^#]*#", match => subs.TryGetValue(
match.Value.Substring(1, match.Value.Length - 2), out var item) ? item : "");
then you can apply it to the file: we read file's lines, process them with a help of Linq and write them into another file.
var dict = new Dictionary<string, string>() {
{"data.tool_context.TOOL_SOFTWARE_VERSION", "sw0.2.002" },
{"data.context.TOOL_ENTITY", "WSM102" },
};
File.WriteAllLines(klarfOutputNameActual, File
.ReadLines(WSMTemplatePath)
.Select(line => MyReplace(line, dict)));
Edit: If you want to switch off MyReplace from some line on
bool doReplace = true;
File.WriteAllLines(klarfOutputNameActual, File
.ReadLines(WSMTemplatePath)
.Select(line => {
//TODO: having line check if we want to keep replacing
if (!doReplace || SomeCondition(line)) {
doReplace = false;
return line;
}
return MyReplace(line, dict)
}));
Here SomeCondition(line) returns true whenever header ends and we should not replace #..# any more.
I have example data like this , the data is in the text file(.txt) sry i got this type of file, if its excel or csv maybe it will be easier
Edit : i make a console app with C#
FamilyID;name;gender;DOB;Place of birth;status
1;nicky;male;01-01-1998;greenland;married
1;sonia;female;02-02-1995;greenland;married
2;dicky;male;04-01-1995;bali;single
3;redding;male;01-05-1996;USA;single
3;sisca;female;05-03-1994;australia;married
i want to take the specific column from that data, for example i want to take FamilyID,Name and status.
I already tried some code to read data and take all the data and list it to new text file.
The goal is to create a new text file based on family ID, and only take specific columns.
The problem is : i cant take a specific column that i want from text file (don't know how to select many column in the code that i write)
DateTime date = DateTime.Now;
string tgl = date.Date.ToString("dd");
string bln = date.Month.ToString("d2");
string thn = date.Year.ToString();
string tglskrg = thn + "/" + bln + "/" + tgl;
string filename = ("C:\\Users\\Documents\\My Received Files\\exampledata.txt");
string[] liness = File.ReadAllLines(filename);
string[] col;
var lines = File.ReadAllLines(filename);
var groups = lines.Skip(1)
.Select(x => x.Split(';'))
.GroupBy(x => x[0]).ToArray();
foreach (var group in groups)
{
Console.WriteLine(group);
File.WriteAllLines(#"C:\\Users\\Documents\\My Received Files\\exampledata_"+group.Key+".txt", group.Select(x => string.Join(";", x)));
}
maybe someone can help? thankyou
One way to approach this would be capture the details to a data structure and later write the required details to file. For example,
public class Detail
{
public int FamilyID{get;set;}
public string Name{get;set;}
public string Gender{get;set;}
public DateTime DOB{get;set;}
public string PlaceOfBirth{get;set;}
public string Status{get;set;}
}
Now you can write a method that parses the string based on delimiter and returns an IEnumerable.
public IEnumerable<Detail> Parse(string source,char delimiter)
{
return source.Split(new []{Environment.NewLine},StringSplitOptions.RemoveEmptyEntries)
.Skip(1)
.Select(x=>
{
var detail = x.Split(new []{delimiter});
return new Detail
{
FamilyID = Int32.Parse(detail[0]),
Name = detail[1],
Gender = detail[2],
DOB = DateTime.Parse(detail[3]),
PlaceOfBirth = detail[4],
Status = detail[5]
};
}
);
}
Client Call
Parse(stringFromFile,';');
Output
Now you can pick and write the details you want to write to output file from the collection.
try this.
var list = new List<String>();
list.Add("FamilyID;name;gender;DOB;Place of birth;status");
list.Add("1;nicky;male;01-01-1998;greenland;married");
list.Add("1;sonia;female;02-02-1995;greenland;married");
list.Add("2;dicky;male;04-01-1995;bali;single");
list.Add("3;redding;male;01-05-1996;USA;single");
list.Add("3;sisca;female;05-03-1994;australia;married");
var group = from item in list.Skip(1)
let splitItem = item.Split(';', StringSplitOptions.RemoveEmptyEntries)
select new
{
FamilyID = splitItem[0],
Name = splitItem[1],
Status = splitItem[5],
};
foreach(var item in group.ToList())
{
Console.WriteLine($"Family ID: {item.FamilyID}, Name: {item.Name}, Status: {item.Status}");
}
I'm trying to find the most time efficient way of classifying expenses on a piece of Accounting Software. The values come in like this:
"EFTPOS Kathmandu 2342342"
I have created a method as follows:
private static string Classifier(string inputDescription)
{
Dictionary<string, string> classified = new Dictionary<string, string>();
classified.Add("D/C FROM", "INCOME" );
classified.Add("CREDIT ATM", "INCOME");
classified.Add("INTEREST", "INCOME");
classified.Add("EFTPOS", "EXPENSE" );
classified.Add("DEBIT DEBIT", "EXPENSE");
classified.Add("CC DEBIT", "EXPENSE");
classified.Add("PAYMENT RECEIVED", "TRANSFER");
classified.Add("PAYMENT - THANK YOU", "TRANSFER");
classified.Add("IRD", "TAX" );
classified.Add("I.R.D", "TAX");
try
{
// What do I do here to get the value?
return value;
}
catch(Exception)
{
return "OTHER";
}
}
Basically, I want to run through the values of my inputDescription against the keys in the dictionary to get its value (the classification of the line item).
So for the example shown above, the result would be "EXPENSE".
I assumed dictionary would be the fastest way to approach this, but open to suggestions on better methods.
Thanks in Advance!
What about using RegEx?
const string EXPENSE_PATTERN = "^(EFTPOS|DEBIT DEBIT|CC DEBIT)"
const string ..._PATTERN
if (Regex.IsMatch(input, EXPENSE_PATTERN)){
return "EXPENSE";
} else if (Regex.IsMatch(input, INCOME_PATTERN)){
return "INCOME";
} else if (Regex.IsMatch(input, ..._PATTERN)){
return "...";
} else {
return "OTHER"
}
One of the way to achieve this is
string input = "EFTPOS Kathmandu 2342342";
string value = string.Empty;
foreach (var key in input.Split(' '))
{
value = classified.Where(k => classified.ContainsKey(k.Key)).Select(k => classified[k.Key]).FirstOrDefault();
if(value != null & value.trim()!= string.empty)
break;
}
Check the value is null or not for further use. foreach loop will break once will find value.
Calling method:
static void Main(string[] args)
{
Console.WriteLine(Classifier("EFTPOS Kathmandu 2342342"));
Console.WriteLine(Classifier("D/C FROM Kathmandu 2342342"));
Console.ReadKey();
}
Classifier method:
private static string Classifier(string inputDescription)
{
var classified = new Dictionary<string, string>
{
{ "D/C FROM", "INCOME" },
{ "CREDIT ATM", "INCOME" },
{ "INTEREST", "INCOME" },
{ "EFTPOS", "EXPENSE" },
{ "DEBIT DEBIT", "EXPENSE" },
{ "CC DEBIT", "EXPENSE" },
{ "PAYMENT RECEIVED", "TRANSFER" },
{ "PAYMENT - THANK YOU", "TRANSFER" },
{ "IRD", "TAX" },
{ "I.R.D", "TAX" }
};
try
{
foreach (var kvp in classified)
if (inputDescription.StartsWith(kvp.Key))
return kvp.Value;
return "OTHER";
}
catch
{
return "OTHER";
}
}
Returns:
EXPENSE
INCOME
Of course you could move the Dictionary definition outside of the method and make it a class member. That would especially make sense if you have multiple frequent calls to Classifier. You could also define it as an IReadOnlyDictionary to prevent changes to its contents.
The easiest way to get something from a dictionary is by using the key like:
Dictionary<string, string> classified = new Dictionary<string, string>();
var value = classified[key];
but of-course you would wanna check for the key occurence in dictionary like:
if(classified.ContainsKey(key))
return classified[key];
else
throw new InvalidTypeException();//this is because you should have all the key's mapped i.e you are only expecting known key types.People prefer other types like they would return null but i throw coz my dictionary is not having this key
Now coming to the values:
All the Values seem to be known and repeated types.So i would build an enum:
enum TransactionType
{
Expense,
Income,
Transfer
}
enum Source
{
EFTPOS,
DEBIT DEBIT,
...so on...
}
i prefer enums to avoid magic strings and people do make mistakes while typing strings.
So with the Combination of Dictionary and enum now i would build as :
private Dictionary<Source,TransactionType> PopulateSource()
{
Dictionary<Source,TransactionType> classified = new Dictionary<Source,TransactionType>();
//populate dictionary by iterating using
var keys = Enum.GetValues(typeof(Source));
var values = Enum.GetValues(typeof(TransactionType));
you can just iterate through keys if your keys and values in enum are in order .
return classified ;
}
public void TestSourceTransaction()
{
TransactionType transType;
var classifieds = PopulateSource();
var key = GetSourceType(inputDescription);//you need to write a method to get key from desc based on regex or string split options.
if(classifieds.ContainsKey(key))
classifieds[key].Value;
else
throw new InvalidTypeException("Source type undefined");
}
I prefer clean and expandable code and absolute no to magic string.
I have a .csv file with a list of abbreviations and their actual meaning e.g.
Laughing Out Loud, LOL
I need to be able to search for an abbreviation in a text box and replace the abbreviation with the actual words. This is what I have attempted so far to understand dictionaries but cannot work out how to read in values from the file.
Dictionary<string, string> Abbreviations = new Dictionary<string, string>();
Abbreviations.Add("Laughing Out Loud", "lol");
foreach (KeyValuePair<string, string> abbrev in Abbreviations)
{
txtinput.Text = txtinput + "<<" + abbrev.Key + ">>";
}
You can try this LINQ solution the GroupBy is to handle the case where a key is in a file multiple times.
Dictionary<string, string[]> result =
File.ReadLines("test.csv")
.Select(line => line.Split(','))
.GroupBy(arr => arr[0])
.ToDictionary(gr => gr.Key,
gr => gr.Select(s => s[1]).ToArray());
To check if the abbreviation in the TextBox exists in the Dictionary:
foreach (KeyValuePair<string, string[]> abbrev in result)
{
if (txtinput.Text == abbrev.Value)
{
txtinput.Text = txtinput + "<<" + abbrev.Key + ">>";
}
}
You can start by creating a Stream Reader for your file, then looping for all your values in the CSV and add them to the dictionary.
static void Main(string[] args)
{
var csv_reader = new StreamReader(File.OpenRead(#"your_file_path"));
//declare your dictionary somewhere outside the loop.
while (!csv_reader.EndOfStream)
{
//read the line and split if you need to with .split('')
var line = reader.ReadLine();
//Add to the dictionary here
}
//Call another method for your search and replace.
SearchAndReplace(your_input)
}
Then have the implementation of that method, search if the input exists in the dictionary and if it does replace it.
You could use LINQ to put the values of the csv into your dictionary, if that's easier for you.
I'm going to assume that your input file may have commas in the actual text, and not just separating the two fields.
Now, if that were the case, then the standard CSV file format for format the file like this:
Laughing Out Loud,LOL
"I Came, I Saw, I Conquered",ICISIC
However, from your example you have a space before the "LOL", so it doesn't appear that you're using standard CSV.
So I'll work on this input:
Laughing Out Loud, LOL
"I Came, I Saw, I Conquered",ICISIC
"to, too, or two", 2
because,B/C
For this input then this code produces a dictionary:
var dictionary =
(
from line in File.ReadAllLines("FILE.CSV")
let lastComma = line.LastIndexOf(',')
let abbreviation = line.Substring(lastComma + 1).Trim()
let actualRaw = line.Substring(0, lastComma).Trim()
let actual = actualRaw.StartsWith("\"") && actualRaw.EndsWith("\"")
? actualRaw.Substring(1, actualRaw.Length - 2)
: actualRaw
select new { abbreviation, actual }
).ToDictionary(x => x.abbreviation, x => x.actual);
You can go one better than this though. It's quite possible to create a "super function" that will do all of the replaces in one go for you.
Try this:
var translate =
(
from line in File.ReadAllLines("FILE.CSV")
let lastComma = line.LastIndexOf(',')
let abbreviation = line.Substring(lastComma + 1).Trim()
let actualRaw = line.Substring(0, lastComma).Trim()
let actual = actualRaw.StartsWith("\"") && actualRaw.EndsWith("\"")
? actualRaw.Substring(1, actualRaw.Length - 2)
: actualRaw
select (Func<string, string>)(x => x.Replace(abbreviation, actual))
).Aggregate((f1, f2) => x => f2(f1(x)));
Then I can do this:
Console.WriteLine(translate("It was me 2 B/C ICISIC, LOL!"));
I get this result:
It was me to, too, or two because I Came, I Saw, I Conquered, Laughing Out Loud!
I am trying to read in POST data to an ASPX (c#) page. I have got the post data now inside a string. I am now wondering if this is the best way to use it. Using the code here (http://stackoverflow.com/questions/10386534/using-request-getbufferlessinputstream-correctly-for-post-data-c-sharp) I have the following string
<callback variable1="foo1" variable2="foo2" variable3="foo3" />
As this is now in a string, I am splitting based on a space.
string[] pairs = theResponse.Split(' ');
Dictionary<string, string> results = new Dictionary<string, string>();
foreach (string pair in pairs)
{
string[] paramvalue = pair.Split('=');
results.Add(paramvalue[0], paramvalue[1]);
Debug.WriteLine(paramvalue[0].ToString());
}
The trouble comes when a value has a space in it. For example, variable3="foo 3" upsets the code.
Is there something better I should be doing to parse the incoming http post variables within the string??
You might want to treat it as XML directly:
// just use 'theResponse' here instead
var xml = "<callback variable1=\"foo1\" variable2=\"foo2\" variable3=\"foo3\" />";
// once inside an XElement you can get all the values
var ele = XElement.Parse(xml);
// an example of getting the attributes out
var values = ele.Attributes().Select(att => new { Name = att.Name, Value = att.Value });
// or print them
foreach (var attr in ele.Attributes())
{
Console.WriteLine("{0} - {1}", attr.Name, attr.Value);
}
Of course you can change that last line to whatever you want, the above is a rough example.