C# Read in multiple profiles from streamreader and give outputs - c#

I have been working on a program that goes along with a game me and some of my friends play, all I have done so far is make the interface, figured I would ask for help in advance because I know ill need it. I wanted to make a GUI that would read in data of a character from a text file, fill text boxes with it and be able to do equations with two of the boxes, then move onto another character from that text file by hitting next.
sample read in data would be :
Interface
Hark <- new character
5
2
6
40.0
12.00
Caro <- new character
6
1
8
38.0
10.00
Ethan <- new character
4
5
42.0
15.00
(The above is not actually code, just how it posted)
So "name" through "char num" would just be populated by data.Readline(); from streamreader, but HP and armor would need to be times-ed together to get ratio (hitting the compute ratio button), all data from name to ratio would go in the text box below the buttons, the next button would cycle to the next character.
Any help would be much appreciated, thanks in advance.

It's going to be a lot easier to do it with XML serialization/deserialization.
Here's a complete demo. It creates a list of two characters, serializes it to XML, then deserializes it back into a new List. This will take care of the storage aspect of it, at least.
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
namespace XmlDemo
{
public class CharacterAttributes
{
public string Name { get; set; }
public int Strength { get; set; }
public int Dexterity { get; set; }
}
class Program
{
static void Main(string[] args)
{
var characters = new List<CharacterAttributes>
{
new CharacterAttributes
{
Name = "Throgdor the Destroyer",
Strength = 5,
Dexterity = 10
},
new CharacterAttributes
{
Name = "Captain Awesome",
Strength = 100,
Dexterity = 9
}
};
SerializeToXML(characters);
var charactersReloaded = DeserializeFromXML(#"C:\temp\characters.xml");
}
static public void SerializeToXML(List<CharacterAttributes> characters)
{
var serializer = new XmlSerializer(typeof(List<CharacterAttributes>));
var textWriter = new StreamWriter(#"C:\temp\characters.xml");
using (textWriter)
{
serializer.Serialize(textWriter, characters);
textWriter.Close();
}
}
public static List<CharacterAttributes> DeserializeFromXML(string path)
{
var serializer = new XmlSerializer(typeof(List<CharacterAttributes>));
var textReader = new StreamReader(path);
var deserializedCharacters = new List<CharacterAttributes>();
using (textReader)
{
deserializedCharacters = serializer.Deserialize(textReader) as List<CharacterAttributes>;
}
return deserializedCharacters;
}
}
}

I would probably do a File.ReadAllText ( or something like that) into a string. That way you release the file handle. Then you can loop over the characters in the string that you read from the file and do whatever you want.

Related

it isn't cohesive, and it doesn't abstract away any of the implementation details

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.

How to get a coordinator (x, y) from specific letter in PDF using iTextSharp?

I'm working with PDF and using iTexhSharp. So far, I could get data from a specific area already. But, I would like to make more flexible by make a it find the coordinator of the first letter(or number) of desired word and then from that coordinator make rectangle to crop around desired word. It would be good if anyone can give me a short example. Thank you.
The basic idea here is to use IEventListener to get notified of TextRenderInfo events. Then split these into CharacterRenderInfo, and then ask for the bounding box of each of those.
class CharacterRenderInfoGetter implements IEventListener {
private List<CharacterRenderInfo> characterRenderInfoList = new ArrayList<>();
#Override
public void eventOccurred(IEventData iEventData, EventType eventType) {
if(eventType == EventType.RENDER_TEXT)
{
TextRenderInfo tri = (TextRenderInfo) iEventData;
for(TextRenderInfo subTri : tri.getCharacterRenderInfos())
{
characterRenderInfoList.add(new CharacterRenderInfo(subTri));
}
}
}
public List<CharacterRenderInfo> getCharacterRenderInfoList()
{
java.util.Collections.sort(characterRenderInfoList);
return characterRenderInfoList;
}
#Override
public Set<EventType> getSupportedEvents() {
return null;
}
}
You can then use this class like so:
File inputFile = getInputFiles()[0]; // provide your own implementation of course
// create an iText PdfDocument out of the File
PdfDocument pdfDocument = new PdfDocument(new PdfReader(inputFile));
// construct the IEventListener that will measure character distances
CharacterRenderInfoGetter characterRenderInfoGetter = new CharacterRenderInfoGetter();
PdfCanvasProcessor processor = new PdfCanvasProcessor(characterRenderInfoGetter);
/* Here we explicitly tell the IEventListener to process page 1 (the first page of the document
* you can loop over all pages if you want to repeat this
*/
processor.processPageContent(pdfDocument.getPage(1));
I know this code is written in Java. But the .NET equivalent should be very similar. At the very least it's good pseudo-code.

Writing to file using properties

I'm trying to write to a file at the moment I create my HighScores object. I'm trying to use the Name and Score properties as the text to the file, but they seem to be null and 0 respectively even though I initialized the object. So my question is why is it not writing "David : 88 "?
static void Main(string[] args)
{
HighScores David = new HighScores() { Name = "David", Score = 88 };
}
class HighScores
{
public string Name { get; set; }
private int score;
public int Score
{
get
{
if (score < 50)
{
return 0;
}
return score;
}
set
{
score = value;
}
}
public HighScores()
{
// Opening and writing to the file
FileStream fileStream = File.OpenWrite(path);
StreamWriter writer = new StreamWriter(fileStream);
writer.Write($"{Name} : {Score} \n");
writer.Close();
}
}
I think the issue is that the Constructor runs before any of the "sets" in your code. Setting breakpoints in your code (in your constructor, in the property sets) and using Step Into, might help see what order all the code is being run in.
So, instead of writing the values in the constructor, refactor that into an actual method.
change the line
public HighScores()
to
public void SaveScores()
then add the line after you "new" up your object.
David.SaveScores();
That should work.
I'd also look into leveraging the using/Dispose pattern as well.
using (var fileStream = File.OpenWrite(path))
{
// do stuff
}
// dotNet will be sure to call Dispose and clean up the fileStream.
As Andre correctly points out, the “Constructor” public HighScores() is called when you create a new HighScores object like you have below.
HighScores David = new HighScores() { Name = "David", Score = 88 };
Unfortunately the properties Name and Score have not been initialized. Since it is a “Constructor” simply pass the variables as you would a normal constructor like below:
HighScores David = new HighScores("David", 88);
Then set the matching signature in the HighScores “Constructor” then you can set the properties and it should work as expected, however I agree with Andre as this (writing to file) should be a separate method and NOT part of the “Constructor” Hope that makes sense.
public HighScores(string name, int score) {
Name = name;
Score = score;
using (FileStream fileStream = File.OpenWrite(path)) {
StreamWriter writer = new StreamWriter(fileStream);
writer.Write($"{Name} : {Score} \n");
writer.Close();
}
}
.

Parsing key value pairs c#

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; }
}

Files,strings and save

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. ;-)

Categories

Resources