I have an input csv file that is read by C#. I want to write a piece of code that scans the file for all 'nulls' and replace with an empty string on the output.
so far I have wrote this. The public FileProcessor() doesnt seem to work
class FileProcessor
{
//File Objects
readonly Dictionary<string, FileInfo> _fileDict;
//DB Objects
private readonly EMIRDB _emirdb;
public FileProcessor()
{
_fileDict = new Dictionary<string, FileInfo>();
_emirdb = new EMIRDB();
}
public FileProcessor()
{
string replacenull = File.ReadAllText ("EMIR_VU_E_");
replacenull = replacenull.Replace("null", "");
File.WriteAllText("EMIR_VU_E_", replacenull);
}
You cannot have two constructors with the same set of parameters (in this case, zero parameters).
Thus:
public FileProcessor()
{
_fileDict = new Dictionary<string, FileInfo>();
_emirdb = new EMIRDB();
}
public FileProcessor()
{
string replacenull = File.ReadAllText ("EMIR_VU_E_");
replacenull = replacenull.Replace("null", "");
File.WriteAllText("EMIR_VU_E_", replacenull);
}
is not allowed.
You need to remove one of the constructors. Or move the code from one constructor into the other.
Related
Using the below code, I'm trying to save app settings data in JSON format so it's easily readable and (in theory) easy to load back directly into data structures. Unfortunately it's not working out that way.
My general strategy is to ahve a series of lists representing different types of settings which I'll drop into one ListDictionary and then save as a single JSON object. Then, in theory, I load it back to a ListDictionary and recast the values into the lists they started as.
// Paths to pin to quick menu in Windows Explorer
public List<string> quickPaths = new List<string>();
public string diag = "";
public string settingsFile = System.AppDomain.CurrentDomain.BaseDirectory + "customizer_settings.json";
public Prefs()
{
ListDictionary prefs = LoadPrefs();
quickPaths = (List<string>)prefs["quickPaths"];
}
public ListDictionary LoadPrefs()
{
if (!File.Exists(settingsFile)) return new ListDictionary();
string json = File.ReadAllText(settingsFile);
return JsonSerializer.Deserialize<ListDictionary>(json);
}
public void SavePrefs()
{
ListDictionary toSave = new ListDictionary();
toSave["quickPaths"] = quickPaths;
File.WriteAllText(settingsFile, JsonSerializer.Serialize(toSave));
}
Instead, I'm getting the error in the title on the quickPaths assignment in the Prefs() constructor. I've looked it up and there's nothing else out there about this error specifically and no workarounds I've been able to find.
I've tried iterating over the prefs["quickPaths"] value and manually adding them one at a time to the List, but that's both inelegant and doesn't work anyway. Clearly I'm doing something wrong, but I don't know what. I thought I would get from deserialize exactly what I serialized, but it seems it doesn't work that way.
Here's what the output of the save function looks like:
{"quickPaths":["C:\\output","C:\\Users","C:\\Windows"]}
Try with the Newtonsoft like this
public class Prefs
{
public List<string> quickPaths = new List<string>();
public string diag = "";
public string settingsFile = System.AppDomain.CurrentDomain.BaseDirectory + "customizer_settings.json";
public Prefs()
{
ListDictionary prefs = LoadPrefs();
quickPaths = ((JArray)prefs["quickPaths"]).ToObject<List<string>>();
}
public ListDictionary LoadPrefs()
{
if (!File.Exists(settingsFile)) return new ListDictionary();
string json = File.ReadAllText(settingsFile);
return JsonConvert.DeserializeObject<ListDictionary>(json);
}
public void SavePrefs()
{
ListDictionary toSave = new ListDictionary();
toSave["quickPaths"] = quickPaths;
File.WriteAllText(settingsFile, JsonConvert.SerializeObject(toSave));
}
}
or you can deserialize one more time, like
quickPaths = JsonSerializer.Deserialize<List<string>>(((JsonElement)prefs["quickPaths"]).GetRawText());
I tried the Newtonsoft method including the deserializing of the sub-element and it didn't work (at least not how I implemented it).
Instead I re-evaluated my data structures and got rid of the ListDictionary in favor of Dictionary<string,List> since that's what I was doing anyway.
From there, I just needed to convert the comma-separated string to a list which can be done with built-in functions like so:
// Paths to pin to quick menu in Windows Explorer
public List<string> quickPaths = new List<string>();
public string diag = "";
public string settingsFile = System.AppDomain.CurrentDomain.BaseDirectory + "customizer_settings.json";
public Prefs()
{
Dictionary<string,List<string>> prefs;
prefs = LoadPrefs();
quickPaths = prefs["quickPaths"].ToList<string>();
}
public Dictionary<string,List<string>> LoadPrefs()
{
if (!File.Exists(settingsFile)) return new Dictionary<string,List<string>>();
string json = File.ReadAllText(settingsFile);
return JsonSerializer.Deserialize<Dictionary<string,List<string>>>(json);
}
public void SavePrefs()
{
Dictionary<string,List<string>> toSave = new Dictionary<string,List<string>>();
toSave["quickPaths"] = quickPaths;
File.WriteAllText(settingsFile, JsonSerializer.Serialize(toSave));
}
I'd like to parse a text file with a few dozen entries. Right now, I have a dumbed-down solution that reads line by line and compares against hard-coded strings:
while ((line = reader.ReadLine()) != null) //returns null if end of stream
{
cmpStr = "MODE";
try
{
if (line.Equals(cmpStr))
GlobalData.mode = Convert.ToInt32(line.Remove(0, cmpStr.Length));
}
catch { }
cmpStr = "TIME_YEAR";
try
{
if (line.Equals(cmpStr))
GlobalData.time_year = Convert.ToInt32(line.Remove(0, cmpStr.Length));
}
catch { }
// ... repeat to parse the remaining lines
}
GlobalData is a static class and looks like this:
public static class GlobalData
{
public static int mode;
public static int time_year;
public static int time_month;
public static int time_day;
public static int time_hour;
public static int time_minute;
// other entries omitted
public static string[] GlobalKeywords = new string[37]
{
"MODE",
"TIME_YEAR",
"TIME_MONTH",
"TIME_DAY",
"TIME_HOUR",
"TIME_MINUTE",
// other entries omitted
};
}
If it were possible to access my static fields by index, I'd do:
int i = 0;
while ((line = reader.ReadLine()) != null)
{
cmpStr = GlobalData.GlobalKeywords[i]; // when i == 0: cmpStr = "MODE"
if (line.Equals(cmpStr))
GlobalData[i] = Convert.ToInt32(line.Remove(0, cmpStr.Length));
// GlobalData[0] would be GlobalData.mode, and so on (but doesn't work)
i++;
}
catch { }
So, even though I can setup a loop to compare against a string array of keywords,
how do I assign a certain field of my static class ?
br
Chris
I'm not sure what your business constraints are, so it's hard to propose a fool-proof solution, though a few points:
cmpStr = "MODE";
try
{
if (line.Equals(cmpStr))
GlobalData.mode = Convert.ToInt32(line.Remove(0, cmpStr.Length));
}
This won't work as you (probably expect) - if line.Equals("MODE") then line.Remove(0, "MODE".Length) is an empty string. What you probably want is line.StartsWith(cmpStr) or line.Contains(cmpStr).
GlobalData is a static class
This doesn't seem a good approach for what you're doing. You may want to read up on static classes and when to use them (MSDN is a good starting point, though it obviously can't cover everything: http://msdn.microsoft.com/en-us/library/79b3xss3%28v=vs.80%29.aspx).
Other than that, you can probably simply replace all your int fields with a dictionary (though please rethink the static approach as described above):
public static Dictionary<String, int> Items = new Dictionary<String, int>();
Then your parsing code could look like this:
while ((line = reader.ReadLine()) != null) //returns null if end of stream
{
var matchingString
= GlobalData.GlobalKeywords.FirstOrDefault(s => line.StartsWith(s));
if (matchingString != null)
GlobalData[matchingString]
= Convert.ToInt32(line.Remove(0, matchingString.Length));
}
You will then be able to fetch that data using e.g. GlobalData.Items["MODE"].
One last bit: you may consider introducing constant values in your global data class, e.g.:
public const String MODE = "MODE";
Then you can use GlobalData.Items[GlobalData.MODE] and avoid typos: writing GlobalData.Items[GlobalData.MODe] would cause a compile error.
Replace this:
public static int mode;
public static int time_year;
public static int time_month;
public static int time_day;
public static int time_hour;
public static int time_minute;
With this:
public static Dictionary<string, int> my_values = new Dictionary<string, int>();
Then replace:
GlobalData[i] = Convert.ToInt32(line.Remove(0, cmpStr.Length));
with:
GlobalData.my_values[cmpStr] = Convert.ToInt32(line.Remove(0, cmpStr.Length));
That should do what you want even though I don't understand how you expect the Convert.ToInt32 to work. The way you are calling Remove will create an empty string (which might convert to 0, I can't remember) and even if it didn't, the line doesn't contain a number because you compared it successfully to a string like "MODE".
An elegant way to solve your problem is to prepare a different action for each of the acceptable strings. You use a Dictionary(Of String, <Action>) where Action is a common delegate type that receive a string in input and know how to process it accordingly to the keyword present at the beginning of the line.
// The common signature for every methods stored in the value part of the dictionary
public delegate void ParseLine(string line);
// Global dictionary where you store the strings as keyword
// and the ParseLine as the delegate to execute
Dictionary<String, ParseLine> m_Actions = new Dictionary<String, ParseLine>() ;
void Main()
{
// Initialize the dictionary with the delegate corresponding to the strings keys
m_Actions.Add("MODE", new ParseLine(Task1));
m_Actions.Add("TIME_YEAR", new ParseLine(Task2));
m_Actions.Add("TIME_MONTH", new ParseLine(Task3));
m_Actions.Add("TIME_DAY", new ParseLine(Task4));
.....
while ((line = reader.ReadLine()) != null)
{
// Search the space that divide the keyword from the value on the same line
string command = line.Substring(0, line.IndexOf(' ')).Trim();
// a bit of error checking here is required
if(m_Actions.ContainsKey(command))
m_Actions[command](line);
}
}
void Task1(string line)
{
// this will handle the MODE line
GlobalData.Mode = Convert.ToInt32(line.Substring(line.IndexOf(' ')+1).Trim());
}
void Task2(string line)
{
GlobalData.time_year = Convert.ToInt32(line.Substring(line.IndexOf(' ')+1).Trim());
}
void Task3(string line)
{
.....
}
void Task4(string line)
{
.....
}
A simple (and not really clean) approach is to add an indexer to your global data class and decide which field to set based on the index. But you have to extend the indexer every time you add a field (basically you move the if/switch from the while Loop into the indexer).
You could also use reflection, if you can match the keyword to the field name. This is not very performant but does not need to be extended as long as you can map the keyword to the new field name.
Another approach is to create a dictionary>. In this dictionary you register the keywords, e.g. (pseudo-code):
Class Level variable:
private keywordsDict = new Dictionary<string, Action<int>>();
In a constructor:
keywordsDict.Add("MODE", delegate(value) GlobalData.mode = value);
In while-loop:
var action = keywordsDict[line];
action(value);
In the later approach, you only need to extend the dictionary but not the algorithm as such if you have a new keyword/field.
May be i can tell you how to achieve it (GlobalData[i]) in C# thought its not the answer you are looking for.
class GlobalData
{
private string[] array = new string[10];
public GlobalData()
{
//now initialize array
array[0] = "SomeThingA";
array[1] = "SomeThingB";//continue initialization.
}
public string this[int index]
{
get {return array[index];}
}
}
Now the clients can use GlobalData like ,
GlobalData gd = new GlobalData();
gd[1] = "SomeOtherThing" ; //set the value.
string value = gd[1];//get the value
But this cant be done by making the class static as you see it works with 'this'
is it possible to create a List of my own class from a CSV file?
the file e.g. looks like:
ID;NAME
1;Foo
2;Bar
then I have a class like:
class MyClass
{
public int id { get; set; }
public string name { get; set; }
}
is it possible to generate a list of this class out of the cvs file? maybe with some library
You could use a CSV parser such as FileHelpers or FastCSV. If you don't want to use third party libraries you may take a look at the built-in TextFieldParser class which could be used like that:
public IEnumerable<MyClass> Parse(string path)
{
using (TextFieldParser parser = new TextFieldParser(path))
{
parser.CommentTokens = new string[] { "#" };
parser.SetDelimiters(new string[] { ";" });
parser.HasFieldsEnclosedInQuotes = true;
// Skip over header line.
parser.ReadLine();
while (!parser.EndOfData)
{
string[] fields = parser.ReadFields();
yield return new MyClass()
{
id = fields[0],
name = fields[1]
};
}
}
}
and then:
List<MyClass> list = Parse("data.csv").ToList();
But never, please never roll your own CSV parser as other people suggested you here as answers to your question.
I managed to write to and read a specific parameter from a .ini file.
I was wondering if there was a way to load the entire content of the .ini file and store it in a special class. That way, I only have to load the .ini file once. This way it should reduce the amount of loading that the game will do.
I know that in small games, it propably doesn't matter, but I would still appreciate it if someone could point me in the right direction.
I believe that the creators of C# tend to push people in the direction of XML based config files rather then INI files - so there isn't anything built in. I found this article on CodeProject that wraps things in a nice class. Will this be of any help?
http://www.codeproject.com/Articles/1966/An-INI-file-handling-class-using-C
I didn't write it - and am not taking credit for it, but it may be what you are looking for :)
Assuming the INI is a simple key / value pair split with new line could you use something like this to provide the entire INI file as either a dictionary or a strongly typed object.
the method allows you to load an ini file into an object like this.
class IniStructure
{
public short Field1;
public int Property1 { get; set; }
public string Property2 { get; set; }
}
IniStructure ini = IniLoader.Load<IniStructure>(<fileName>);
or simply in to a dictionary with the non T method.
public static class IniLoader
{
public static T Load<T>(string fileName)
{
T results = (T)Activator.CreateInstance(typeof(T));
PropertyInfo[] tProperties = typeof(T).GetProperties();
FieldInfo[] tFields = typeof(T).GetFields();
var iniFile = Load(fileName);
foreach (var property in tProperties)
if (iniFile.ContainsKey(property.Name))
{
object s = System.Convert.ChangeType(iniFile[property.Name].ToString(), property.PropertyType);
property.SetValue(results, s, null);
}
foreach (var field in tFields)
if (iniFile.ContainsKey(field.Name))
{
object s = System.Convert.ChangeType(iniFile[field.Name].ToString(), field.FieldType);
field.SetValue(results, s);
}
return results;
}
public static Dictionary<string, object> Load(string fileName)
{
Dictionary<string, object> results = new Dictionary<string, object>();
string fileText = File.ReadAllText(fileName);
string[] fileLines = fileText.Split('\r');
if (fileLines.Length > 0)
for (int i = 0; i < fileLines.Length; i++)
{
string line = fileLines[i].Trim();
if (!string.IsNullOrEmpty(line))
{
int equalsLocation = line.IndexOf('=');
if (equalsLocation > 0)
{
string key = line.Substring(0, equalsLocation).Trim();
string value = line.Substring(equalsLocation + 1, line.Length - equalsLocation - 1);
results.Add(key, value);
}
}
}
return results;
}
}
Guys i'm really embaraced to ask for this, but i got stuck and i can't think for solution:
I found an IniParser library that i use for parsing settings from file. However the default approach is this:
FileIniDataParser parser = new FileIniDataParser();
//Parse the ini file
IniData parsedData = parser.LoadFile("TestIniFile.ini");
parsetData['some']['settings'];
In my project though, i don't want to call everywhere the parser, so i made a class that load up the file at start-up and all i have to do is just access the instance. This is what i want:
namespace my_project
{
public class settings
{
public IniData ini()
{
string login = "Settings.ini";
FileIniDataParser parser = new FileIniDataParser();
// Parse the ini file with the settings
IniData settings = parser.LoadFile(login);
//*strptr = settings;
return settings;
}
}
}
so i could access the settings like this:
settings.ini['some']['settings'];
Bit i get an error:
Error 329 'settings.ini()' is a
'method' but is used like a 'type'
I know its probably too noobish, but im currently trying to learn C# and experiments like this teach more than reading a 500 page book.
You defined "ini" as a method. In order to use it, as-is, you would need to type:
settings.ini()['some']['settings'];
You could change this to a property to work around this issue.
However, one thing to be aware of - the way you have this written, every time you call ini(), you're creating (and accessing) a new instance of your IniData class. If this is meant to be a single, shared instance, you may want to use lazy initialization with a static property:
public class Settings
{
private static IniData ini;
public static IniData Ini
{
get
{
if (ini == null)
{
string login = "Settings.ini";
FileIniDataParser parser = new FileIniDataParser();
ini = parser.LoadFile(login);
}
return ini;
}
}
}
This way, each call will use the same instance, and it will only create the IniData class one time. You could then write:
Settings.Ini['some']['settings'];
var foo = Settings.Data['some']['settings'];
// ..
public static class Settings
{
static Settings()
{
string login = "Settings.ini";
FileIniDataParser parser = new FileIniDataParser();
_data = parser.LoadFile(login);
}
private static readonly IniData _data;
public static IniData Data
{
get { return _data; }
}
}
Try making your ini method a property:
namespace my_project
{
public class settings
{
public IniData Ini
{
get
{
string login = "Settings.ini";
FileIniDataParser parser = new FileIniDataParser();
// Parse the ini file with the settings
IniData settings = parser.LoadFile(login);
//*strptr = settings;
return settings;
}
}
}
}
To access your instance as
settings.ini['some']['settings'];
You need to make ini a property:
public IniData ini
{
get
{
string login = "Settings.ini";
FileIniDataParser parser = new FileIniDataParser();
// Parse the ini file with the settings
IniData settings = parser.LoadFile(login);
return settings;
}
}
The way you have it now, you must access it as:
settings.ini()['some']['settings'];