Let's say I have a directory with a set of XML files (for example, two files called ReadMacAddress.xml and ReadManufacturerId.xml) that I need to handle each one in a special way. Basically each of these XML files are a set of commands that are being received by my class.
Suppose I have an external class that is giving commands about which file should be opened,
public static void test()
{
string RecievedCommand = "ReadMacAddress"; //Command recieved from a queue
string baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
string xml = File.ReadAllText(baseDirectory + "ReadMacAddress.xml");
}
To have the possibility of opening the file automatically based on which command is received I was thinking of doing the following, first defining an enum data structure with the names of the files and then using a switch case to differentiate between the different commands(name of the file that needs to be parsed) and then using this while parsing the file.
class My_EnumXML
{
public enum ReadXML
{
ReadMacAddress,
ReadManufacturerId,
}
}
class TestRead
{
public static void OpenFile()
{
string RecievedCommand = "ReadMacAddress";
string CurrentCommand = SMLReadInputs((My_EnumXML.ReadXML)RecievedCommand);
string xml = File.ReadAllText(baseDirectory + CurrentCommand);
}
public string SMLReadInputs(My_EnumXML.ReadXML pRecievedCommand)
{
string CurrentCommand = "";
switch (pRecievedCommand)
{
case My_EnumXML.ReadXML.ReadMacAddress:
CurrentCommand = Enum.GetName(typeof(My_EnumXML.ReadXML), 0);
case My_EnumXML.ReadXML.ReadMacAddress:
CurrentCommand = Enum.GetName(typeof(My_EnumXML.ReadXML), 0);
}
return CurrentCommand;
}
}
For this example I just used the name of two XML files but I have a 100 of these and I need to know if I am proceeding in the right way, especially since I cannot debug my code because the part about receiving commands from a message queue is being implemented by someone else.
Related
My professor doesn't want all my code in one class. I am new to C# as well so I don't know how to make my code cohesive and have it abstract away any of the implementation details. Here is my code I have so far. I am trying to make multiple classes because this class has too many responsibilities and I don't know how to do that.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
namespace SvgGenerator
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please enter the name of the output file.");
string outputFile = Console.ReadLine() + ".svg";
Console.WriteLine("Do you want to manually enter the squares or read them from a file? Man or File?");
string fileRead = Console.ReadLine();
if (fileRead.Trim() == "Manually" || fileRead.Trim() == "manually" || fileRead.Trim() == "Man" || fileRead.Trim() == "man")
{
ManInput(outputFile);
}
if (fileRead.Trim() == "file" || fileRead.Trim() == "File")
{
FileInput(outputFile);
}
}
private static void FileInput(string outputFile)
{
Console.WriteLine("What is the name of the file?");
string titleFileName = Console.ReadLine();
StreamReader reader;
reader = new StreamReader(titleFileName);
string textFile = reader.ReadToEnd();
reader.Close();
string[] values = textFile.Split(',', '\n');
List<Square> squares = new List<Square>();
for (int i = 0; i < values.Length;)
{
int valueNumsX = int.Parse(values[i].Trim());
int valueNumsY = int.Parse(values[i + 1].Trim());
Square squareQ = new Square(Color.FromName(values[i + 2].Trim()), valueNumsX, valueNumsY);
squares.Add(squareQ);
if (i == values.Length - 3)
{
SvgBuilder svgBuilder = new SvgBuilder();
string SVG = svgBuilder.Build(squares);
FileCreator Myfilecreater = new FileCreator();
Myfilecreater.Create(outputFile, SVG);
}
i = i + 3;
}
}
private static void ManInput(string outputFile)
{
Console.WriteLine("How many squares do you want in your SVG file?");
string squareCount = Console.ReadLine();
int numSquareCount = Convert.ToInt32(squareCount);
Console.WriteLine("What are the colors of your squares?");
string[] squareColor = new string[numSquareCount];
List<Square> squares = new List<Square>();
for (int i = 0; i < numSquareCount; i++)
{
squareColor[i] = Console.ReadLine();
Square squareQ = new Square(Color.FromName(squareColor[i]), i*4, 0, 200);
squares.Add(squareQ);
if (i == numSquareCount - 1)
{
SvgBuilder svgBuilder = new SvgBuilder();
string SVG = svgBuilder.Build(squares);
FileCreator Myfilecreater = new FileCreator();
Myfilecreater.Create(outputFile, SVG);
}
}
}
}
}`
First of all you should separate classes or methods handling input from classes handling output. If is also typically a poor idea to mix UI from the functional parts, even if the the UI is a console for this case.
I would suggest using the following methods:
private static IEnumerable<Square> ReadSquaresFromFile(string filePath)
private static IEnumerable<Square> ReadSquaresFromConsole()
private static WriteToFile(IEnumerable<Square> squares, string filePath)
For such a simple program procedural programming should be just fine. You do not have to use object. But if you want to, you could for example create a interface like:
public interface ISquareSource(){
IEnumerable<Square> Get();
}
With a file-implementation, console-implementation etc.
Note that I have used string filePath as the file source/destination. If you ever write a library or API, please ensure you have an overlay that takes a stream. It is super annoying to have some data in memory and being forced to write it to a temporary file just because some developer only imagined reading from actual files.
You could also consider using switch statements for handling input, for example
switch(fileRead.Trim().ToLower()){
case "manually":
...
break;
case "file":
...
break;
default:
Console.WriteLine("Invalid input, expected 'manually' or 'file'
break;
Cohesion is the idea that code that does the same thing belongs together, and code that doesn't do the same thing doesn't belong together.
So, let's consider the FileInput function. At a glance, we can see that it does the following:
Prompts the user for a file name to load.
Opens the file.
Reads all of its content into memory.
Closes the file.
Parses the file content into an array of strings.
For each item in the array:
Parses some integral values.
Creates a Square object from those values.
If the index of the current item is equal to the length of the array less 3:
Instantiates a new SvgBuilder.
Invokes its Build method.
Instantiates a new FileCreator.
Invokes its Create method.
There's a lot going on here. Essentially, there are three separate kinds of work going on here that could be broken out into individual functions:
User input (arguably this could be part of the main function).
Call file deserialization function (reads the input file into memory and returns it as an array of strings).
Call main processing function (iterates over the array)
Performs calculations and creates of Square object.
If index of the current item is array length less 3:
Call Build SVG File function
Call File Creation function.
This is what your instructor is getting at.
I've been having issues attempting to parse key value pairs from a text file. I've been scouring for libraries that can do what I'd like as I do not have the ability to create a class that can do this.
Here is the beginning of my file along with a portion of commented out text and key value pairs:
#!version:1.0.0.1
##File header "#!version:1.0.0.1" can not be edited or deleted, and must be placed in the first line.##
#######################################################################################
## Account1 Basic Settings ##
#######################################################################################
account.1.enable = 1
account.1.label = Front
account.1.display_name = Front
What I'm looking to do is grab these values, and be able to update them to within the file in the same location in the file that they are as these files need to remain human readable.
I've looked into Nini as this library seems to be able to do what I'd like, however the error I continue to have is based off of the line 1 of my file as it is not a key value pair.
Expected assignment operator (=) - Line: 1, Position: 19.
I read through the source of Nini, and it seems there is a way to condition the reader to use Mysqlstyle, which would use "#" as comments, but I'm unsure how to adjust it or if it is done automatically as it is completely over my head.
I understand that my files aren't legitimate ini files and there is probably a limitation within the Nini library as it searches for the section that the key value pairs are in.
The code I've attempted to use to parse and display this text to edit with Nini is as follows:
public void EditCFG(string file)
{
if (!string.IsNullOrWhiteSpace(file))
{
IniConfigSource inifile = new IniConfigSource(file);
account_1_display_name.Text = inifile.Configs[""].Get("account.1.display.name");
}
}
Could someone please point me in the right direction?
EDIT
Thanks to #rowland-shaw, I have found the solution:
private IConfigSource source = null;
public void EditCFG(string file)
{
if (!string.IsNullOrWhiteSpace(file))
{
IniDocument inifile = new IniDocument(file, IniFileType.MysqlStyle);
source = new IniConfigSource(inifile);
account_1_display_name.Text = source.Configs["account"].Get("account.1.display_name");
}
}
However, this wasn't completely the answer. I had to also implement sections within the file. After testing my equipment that grabs these files with the updated text, everything was a success.
You need to specify the IniFileType, i.e.:
IniConfigSource inifile = new IniConfigSource(file, IniFileType.MysqlStyle);
Long example:
IniDocument inifile = new IniDocument(file, IniFileType.MysqlStyle);
IniConfigSource source = new IniConfigSource(inifile);
If that is how the format is going to be (key = value and # for comments) in the file, you could do the following (c# pseudocode-ish, you can do the trivial stuff yourself):
Dictionary<string, string> dictionary;
foreach(string line in file)
{
if(string.IsNullOrWhiteSpace(line)) continue;
// Remove extra spaces
line = line.Trim();
if(line[0] == '#') continue;
string[] kvp = line.Split('=');
dictionary[kvp[0].Trim()] = kvp[1].Trim(); // kvp[0] = key, kvp[1] = value
}
Then you can use the created dictionary like account_1_display_name.Text = dictionary["account.1.display.name"];
i can recommend my library Nager.ConfigParser you can easily obtain them over nuget.
Here the example for your configuration
var config = "#comment1\r\naccount.1.enable = 1\r\naccount.1.label = Front";
var configConvert = new ConfigConvert();
var item = configConvert.DeserializeObject<AccountCollection>(config);
public class AccountCollection
{
[ConfigKey("account.")]
[ConfigArray]
public Account[] Accounts { get; set; }
}
public class Account : ConfigArrayElement
{
public int Enable { get; set; }
public string Label { get; set; }
[ConfigKey("display_name")]
public string DisplayName { get; set; }
}
I been having trouble trying to figure this out. When I think I have it I get told no. Here is a picture of it.
I am working on the save button. Now after the user adds the first name, last name and job title they can save it. If a user loads the file and it comes up in the listbox, that person should be able to click on the name and then hit the edit button and they should be able to edit it. I have code, but I did get inform it looked wackey and the string should have the first name, last name and job title.
It is getting me really confused as I am learning C#. I know how to use savefiledialog but I am not allowed to use it on this one. Here is what I am suppose to be doing:
When the user clicks the “Save” button, write the selected record to
the file specified in txtFilePath (absolute path not relative) without
truncating the values currently inside.
I am still working on my code since I got told that it will be better file writes records in a group of three strings. But this is the code I have right now.
private void Save_Click(object sender, EventArgs e)
{
string path = txtFilePath.Text;
if (File.Exists(path))
{
using (StreamWriter sw = File.CreateText(path))
{
foreach (Employee employee in employeeList.Items)
sw.WriteLine(employee);
}
}
else
try
{
StreamWriter sw = File.AppendText(path);
foreach (var item in employeeList.Items)
sw.WriteLine(item.ToString());
}
catch
{
MessageBox.Show("Please enter something in");
}
Now I can not use save or open file dialog. The user should be able to open any file on the C,E,F drive or where it is. I was also told it should be obj.Also the program should handle and exceptions that arise.
I know this might be a noobie question but my mind is stuck as I am still learning how to code with C#. Now I have been searching and reading. But I am not finding something to help me understand how to have all this into 1 code. If someone might be able to help or even point to a better web site I would appreciate it.
There are many, many ways to store data in a file. This code demonstrates 4 methods that are pretty easy to use. But the point is that you should probably be splitting up your data into separate pieces rather than storing them as one long string.
public class MyPublicData
{
public int id;
public string value;
}
[Serializable()]
class MyEncapsulatedData
{
private DateTime created;
private int length;
public MyEncapsulatedData(int length)
{
created = DateTime.Now;
this.length = length;
}
public DateTime ExpirationDate
{
get { return created.AddDays(length); }
}
}
class Program
{
static void Main(string[] args)
{
string testpath = System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "TestFile");
// Method 1: Automatic XML serialization
// Requires that the type being serialized and all its serializable members are public
System.Xml.Serialization.XmlSerializer xs =
new System.Xml.Serialization.XmlSerializer(typeof(MyPublicData));
MyPublicData o1 = new MyPublicData() {id = 3141, value = "a test object"};
MyEncapsulatedData o2 = new MyEncapsulatedData(7);
using (System.IO.StreamWriter w = new System.IO.StreamWriter(testpath + ".xml"))
{
xs.Serialize(w, o1);
}
// Method 2: Manual XML serialization
System.Xml.XmlWriter xw = System.Xml.XmlWriter.Create(testpath + "1.xml");
xw.WriteStartElement("MyPublicData");
xw.WriteStartAttribute("id");
xw.WriteValue(o1.id);
xw.WriteEndAttribute();
xw.WriteAttributeString("value", o1.value);
xw.WriteEndElement();
xw.Close();
// Method 3: Automatic binary serialization
// Requires that the type being serialized be marked with the "Serializable" attribute
using (System.IO.FileStream f = new System.IO.FileStream(testpath + ".bin", System.IO.FileMode.Create))
{
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf =
new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
bf.Serialize(f, o2);
}
// Demonstrate how automatic binary deserialization works
// and prove that it handles objects with private members
using (System.IO.FileStream f = new System.IO.FileStream(testpath + ".bin", System.IO.FileMode.Open))
{
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf =
new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
MyEncapsulatedData o3 = (MyEncapsulatedData)bf.Deserialize(f);
Console.WriteLine(o3.ExpirationDate.ToString());
}
// Method 4: Manual binary serialization
using (System.IO.FileStream f = new System.IO.FileStream(testpath + "1.bin", System.IO.FileMode.Create))
{
using (System.IO.BinaryWriter w = new System.IO.BinaryWriter(f))
{
w.Write(o1.id);
w.Write(o1.value);
}
}
// Demonstrate how manual binary deserialization works
using (System.IO.FileStream f = new System.IO.FileStream(testpath + "1.bin", System.IO.FileMode.Open))
{
using (System.IO.BinaryReader r = new System.IO.BinaryReader(f))
{
MyPublicData o4 = new MyPublicData() { id = r.ReadInt32(), value = r.ReadString() };
Console.WriteLine("{0}: {1}", o4.id, o4.value);
}
}
}
}
As you are writing the employee objects with WriteLine, the underlying ToString() is being invoked. What you have to do first is to customize that ToString() methods to fit your needs, in this way:
public class Employee
{
public string FirstName;
public string LastName;
public string JobTitle;
// all other declarations here
...........
// Override ToString()
public override string ToString()
{
return string.Format("'{0}', '{1}', '{2}'", this.FirstName, this.LastName, this.JobTitle);
}
}
This way, your writing code still keeps clean and readable.
By the way, there is not a reverse equivalent of ToSTring, but to follow .Net standards, I suggest you to implement an Employee's method like:
public static Employee Parse(string)
{
// your code here, return a new Employee object
}
You have to determine a way of saving that suits your needs. A simple way to store this info could be CSV:
"Firstname1","Lastname 1", "Jobtitle1"
" Firstname2", "Lastname2","Jobtitle2 "
As you can see, data won't be truncated, since the delimiter " is used to determine string boundaries.
As shown in this question, using CsvHelper might be an option. But given this is homework and the constraints therein, you might have to create this method yourself. You could put this in Employee (or make it override ToString()) that does something along those lines:
public String GetAsCSV(String firstName, String lastName, String jobTitle)
{
return String.Format("\"{0}\",\"{1}\",\"{2}\"", firstName, lastName, jobTitle);
}
I'll leave the way how to read the data back in as an exercise to you. ;-)
I am trying to create a program to copy all the files from one directory to another. But I am running in a basic issue. It says indentifier expected when I try to compile on line 52.
public bool RecursiveCopy()
{
string origDir = #"D:\Documents and Settings\Dub\My Documents\HoN Updates\test";
string destDir = #"C:\Games\HoN";
bool status = false;
//get all the info about the original directory
var dirInfo = new DirectoryInfo(origDir);
//retrieve all the _fileNames in the original directory
var files = dirInfo.GetFiles(origDir);
//always use a try...catch to deal
//with any exceptions that may occur
try
{
//loop through all the file names and copy them
foreach (string file in Directory.GetFiles(origDir))
{
var origFile = new FileInfo(file);
var destFile = new FileInfo(file.Replace(origDir, destDir));
//copy the file, use the OverWrite overload to overwrite
//destination file if it exists
System.IO.File.Copy(origFile.FullName, destFile.FullName, true);
//TODO: If you dont want to remove the original
//_fileNames comment this line out
File.Delete(origFile.FullName);
status = true;
}
Console.WriteLine("All files in " + origDir + " copied successfully!");
}
catch (Exception ex)
{
status = false;
//handle any errors that may have occurred
Console.WriteLine(ex.Message);
}
return status;
}
public string origDir = #"D:\Documents and Settings\Dub\My Documents\HoN Updates\test"; // ERROR HERE
public string destDir = #"C:\Games\HoN"; // ERROR HERE
private static void RecursiveCopy(origDir, destDir)
{
Console.WriteLine("done");
Console.ReadLine();
}
You did not give type identifiers to your argument list here
static void RecursiveCopy(origDir, destDir)
should be
static void RecursiveCopy(string origDir, string destDir)
Your method RecursiveCopy has two parameters listed without their types. It should be this:
static void RecursiveCopy(string origDir, string destDir)
Here is your problem:
static void RecursiveCopy(origDir, destDir)
You don't specify the types for the parameters, perhaps you intended the following:
static void RecursiveCopy(string origDir, string destDir)
There are more issues however that I've noticed. It's possible you're still working on these, but from what you've posted:
You never call your RecursiveCopy method. Perhaps you meant to call it from Main() instead of declaring an overload with two parameters?
You declare two public fields origDir and destDir but then never use them. Instead you create two local variables in RecursiveCopy() and use these instead. Did you intend to create parameters or use the public fields instead?
Your copy is not actually true to its name of "recursive".
cYou are missing the parameter types in the RecursiveCopy method declaration. Just Change
static void RecursiveCopy(origDir, destDir)
to
static void RecursiveCopy(String origDir, String destDir)
and all is fine.
I am using C#.
I am trying to pull in a text file to an object. I am using an ODBC connection and it looks like this
Driver={Microsoft Text Driver (*.txt; *.csv)};Dbq=C:\Users\Owner\Desktop\IR\IR_Files\Absolute;Extensions=asc,csv,tab,txt;
I am able to make the connection but I can't get my columns separated. I'm using a schema.ini file but it isn't working. Here is my schema file.
[MyTextFile.CSV]
Format=Delimited(|)
ColNameHeader=False
Col1=fullstockn Text
col2=FULLINFO Text
MaxScanRows=0
CharacterSet=ANSI
The text file looks like this.
fullstockn|FULLINFO
"555555 "|Contenu : Neuf Ttudes sur l Some more text here.....
I use the following connection string
string connectionString = String.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties=\"text;HDR=YES;Format=Delimited(|)\";", Path.GetDirectoryName(path));
and a Schema.ini file that typically begins
[myFile.txt]
Format=Delimited(|)
TextDelimiter="none"
and I'll execute a reader via
command.CommandText = String.Format("SELECT * FROM [{0}]", Path.GetFileName(path));
OleDbDataReader reader = command.ExecuteReader();
Also, the MSDN page on the text file driver was helpful when I first investigated this. Specifically, the page on the Schema.ini file is quite useful.
Is there a reason you need to use an ODBC connection for this? I would think it'd be easier to just open the text file directly and parse it yourself.
I don't know if this matters but...
You might be missing the ending "\" in your dbq attribute...
EDIT: Actually...in the text you posted, you have 3 columns, not 2...(2 pipes instead of 1)
I always write the code myself for this kind of op. Here is an example of an abstract class I wrote for this purpose not so long ago. You could modify it or subclass it if you like
public abstract class ReadTextFile : ILoadable
{
public string Path { get; set; }
public UploadFileType FileType { get; set; }
protected internal List<List<string>> Table { get; set; }
public Guid BatchID { get; set; }
/// <summary>
/// Method that loads the raw text into a collection of strings
/// </summary>
/// <returns></returns>
public bool Load()
{
Table = new List<List<string>>();
var splitter = Convert.ToChar("\t");
try
{
using (TextReader tr = new StreamReader(Path))
{
// Discard the first line
String line = tr.ReadLine();
// Read and display lines from the file until the end of the file is reached.
while ((line = tr.ReadLine()) != null)
{
Table.Add(line.Split(splitter).ToList<string>());
}
tr.Close();
tr.Dispose();
}
return true;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return false;
}
}
public string Left(string param, int length)
{
//we start at 0 since we want to get the characters starting from the
//left and with the specified lenght and assign it to a variable
string result = param.Substring(0, length);
//return the result of the operation
return result;
}
public string Right(string param, int length)
{
//start at the index based on the lenght of the sting minus
//the specified lenght and assign it a variable
string result = param.Substring(param.Length - length, length);
//return the result of the operation
return result;
}
}
Try using this connection string
Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Users\Owner\Desktop\IR\IR_Files\Absolute\MyTextFile.CSV;Extended Properties='text'
and:
Beware of the number of columns
Place the schema.ini in the same folder of the executable.