I'm trying to read every #define in a C++ header, and display an editor for each of the values, so I can quickly redefine the constants.
I'm trying to parse the file, getting each item on the left side to be key. I want to display the value on the left, edit the values and save back out. Currently, when I save my file it messes up the format.
This is the code i currently have :
private String header;
private Dictionary<String, String> tokens;
private String file = Properties.Settings.Default.path_location;
public TextBox elf_naparm_zombie_developer;
public harrs_gsh_editor(string file)//TextBox elf_naparm_zombie_developer
{
Properties.Settings.Default.path_location = file;
Properties.Settings.Default.Save();
loadweapon();
// this.elf_naparm_zombie_developer = elf_naparm_zombie_developer;
}
public void loadweapon()
{
this.tokens = File.ReadLines(this.file)
.Select(line => Regex.Match(line, #"^\s*#define\s+(.+?)(\s+(.+?))?((\s*//)|$)"))
.Where(m => m.Success && m.Groups[3].Success)
.ToDictionary(m => m.Groups[1].Value, m => m.Groups[3].Value);
}
public String Search(String name)
{
foreach (KeyValuePair<String, String> pair in tokens)
{
if (pair.Key == name)
return pair.Value;
}
return null;
}
public void Set(String key, String val)
{
tokens[key] = val;
}
public void Save(String file)
{
using (StreamWriter sw = new StreamWriter(File.OpenWrite(file)))
{
sw.Write(header);
foreach (KeyValuePair<String, String> pair in tokens)
{
sw.Write("\r\n" + pair.Key + "#define " + pair.Value);
}
}
}
public void Save()
{
this.Save(file);
}
The issue I have is when I save it back, it messes up the file and removes the #define. What im trying to do is just change the value and write it back to the program with changed value
this is the file i want to edit
So what im trying to do is load up the selected key and display the value witch i have managed to do with this code i created but the issue im having is writing it back
the code i use to load and populate the text box
namespace Harry_s_Template_Editor
{
public partial class Form1 : Form
{
harrs_gsh_editor elfghc;
string path = System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + #"/hb21_zm_ai_napalm.gsh";
public Form1()
{
InitializeComponent();
elfghc = new harrs_gsh_editor(path);//TextBox elf_naparm_zombie_developer
elf_naparm_zombie_developer = elf_naparm_zombie_developer;
// MessageBox.Show(path);
}
private void button1_Click(object sender, EventArgs e)
{
elfghc.loadweapon();
this.elf_naparm_zombie_developer.Text = elfghc.Search("NAPALM_ZOMBIE_DEVELOPER_DEBUG_PRINTS");
elfghc.Set("NAPALM_ZOMBIE_DEVELOPER_DEBUG_PRINTS", elf_naparm_zombie_developer.Text);
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button2_Click(object sender, EventArgs e)
{
elfghc.Set("NAPALM_ZOMBIE_DEVELOPER_DEBUG_PRINTS", elf_naparm_zombie_developer.Text);
elfghc.Save(path);
}
}
}
when i save out the file i now get this error when i try to load the program again
error
thanks in advance Yuki
file when saved back
Original answer:
Why are you writing it back to the file in this order: sw.Write("\r\n" + pair.Key + "#define " + pair.Value);? Shouldn't the #define come first on the line after \r\n? I would have thought it should be something like this: sw.Write("\r\n#define " + pair.Key + " " + pair.Value);
New answer (the question has changed)
There are two problems with what you are doing:
You are writing key/value pairs back to a file, ignoring the headers and credits that were in the original file. If you're happy to strip those out then that's fine, but it sounds like this is important to you. So instead you need to read the file, find the keys in the file, then update the values and save the updated file back to disk. That's a harder problem than simply writing a bunch of key/value pairs to a file.
The second issue is the way you are writing the file back to disk. According to this MSDN page, File.OpenWrite has the following behaviour:
The OpenWrite method opens a file if one already exists for the file
path, or creates a new file if one does not exist. For an existing
file, it does not append the new text to the existing text. Instead,
it overwrites the existing characters with the new characters. If you
overwrite a longer string (such as “This is a test of the OpenWrite
method”) with a shorter string (such as “Second run”), the file will
contain a mix of the strings (“Second runtest of the OpenWrite
method”).
So this is why you're getting a mangled file. You're overwriting the first N bytes with your updated key/value pairs, and the rest of the file is duplicated data from the original file. You probably want to use the following instead, which will overwrite the original file entirely:
using (StreamWriter sw = new StreamWriter(file, false))
Related
I write codes to receive the path of a text file and store it in a string variable that I declare in public.
Then I want to know if the file exists or not by using
System.IO.File.Exists(pathoffile)
But it always returns false even though there is a file.
And then when I try to add the string path directly like this
public string propertyfile = #"C:\Users\PFA Wongsawat\Desktop\part_no_and_path_list.txt"
The function
System.IO.File.Exists(pathoffile)
return true
I already check the receive path(string) that I read from the text file. By cutting off "\n" and "\r" and using trim() too.But it still returns false.
Have I missed something? What difference between these two?. I'm too new to this c#. I'm very bad at this sorry in advance.
Here are my codes
public string pathfromread, partnumber, pathfile, portname, partnofromserial,propertypathfile; //Declare Variables
public string propertyfile = #"C:\Users\PFA Wongsawat\Desktop\Properties.txt";
public string pathoffile ;
public string backuppath ;
public string pdffolderpath ;
private void propertyget()
{
if (File.Exists(propertyfile))
{
StreamReader readpropertyfile = new StreamReader(propertyfile);
string readproperty;
while ((readproperty = readpropertyfile.ReadLine()) != null)
{
string[] propertyfromread = readproperty.Trim().Split('=');
if (propertyfromread.GetValue(0).ToString() == "pathoffile")
{
pathoffile = propertyfromread.GetValue(1).ToString();
pathoffile = pathoffile.Replace("\n", "").Replace("\r", "");
MessageBox.Show(pathoffile, "path file");
}
else if ((propertyfromread.GetValue(0).ToString() == "backuppath"))
{
backuppath = propertyfromread.GetValue(1).ToString();
backuppath = backuppath.Replace("\n", "").Replace("\r", "");
MessageBox.Show(backuppath);
}
else if ((propertyfromread.GetValue(0).ToString() == "pdffolderpath"))
{
pdffolderpath = propertyfromread.GetValue(1).ToString();
pdffolderpath = pdffolderpath.Replace("\n", "").Replace("\r", "");
MessageBox.Show(pdffolderpath);
}
else if ((propertyfromread.GetValue(0).ToString() == "portname"))
{
portname = propertyfromread.GetValue(1).ToString();
portname = portname.Replace("\n", "").Replace("\r", "");
MessageBox.Show(portname);
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
propertyget();
dv = dt.DefaultView; //set dv index count to != 0 to prevent error from null input when click on remove button
if (System.IO.File.Exists(pathoffile))//Check if file exist or not
{
}
else
{
try
{
MessageBox.Show("Database Text File Missing. Please Select New File", "Database Text File Missing", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
OpenFileDialog regispath = new OpenFileDialog();
regispath.Title = "Select Database Text File (part_no_and_path_list.txt)";
regispath.Multiselect = false;
regispath.Filter = "Text file (*.txt)|*.txt";
regispath.RestoreDirectory = true;
regispath.ShowDialog();
pathfile = regispath.FileName;
File.Copy(pathfile, pathoffile);
}
catch
{
And this is my property text file
pathoffile=#"C:\Users\PFA Wongsawat\Desktop\part_no_and_path_list.txt"
backuppath=#"C:\Users\PFA Wongsawat\Documents\part_no_and_path_list.txt"
pdffolderpath=#"C:\Users\PFA Wongsawat\Downloads\"
portname=COM3
In this case the result always a messageBox showing "Database Text File Missing. Please Select New File"
Thank you and sorry for my bad English.
You don't put #" and " in the text file, you only put them in the code because that's how the c# compiler knows they're strings (and knows not to interpret slashes as an escape character)
Just make your text file look like:
pathoffile=C:\Users\PFA Wongsawat\Desktop\part_no_and_path_list.txt
I also recommend you use:
Split(new []{'='}, 2)
This will allow you to use = in your path, by making split return a maximum of 2 split values; any = that are legitimately in the path would be preserved
Actually I recommend you use one of the various built in settings mechanisms that c# has; we haven't needed to read and write our own configuration files for about 25 years
If you really do want to continue rolling your own you can reduce your code massively by using a dictionary
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
public class Settings{
private Dictionary<string,string> _conf = new Dictionary<string,string>();
public string PathOfFile {
get => _conf["pathoffile"];
}
public void ReadConfig(){
File.ReadAllLines("conf.txt").ToDictionary(
x => x.Split(new[]{'='},2)[0],
x => x.Split(new[]{'='},2)[1]
);
}
}
Yep, it's all you need. Every time you want to add another setting, add another property (like public string PathOfFile), add another love to the file and make sure the string in the property matches the line in the file
In other areas, please read up on c# naming conventions; PublicThingsAreNamedLikeThis, _privateLikeThis, localLikeThis, neverlikethis
Thank you I've already solved this problem
By remove "#" and '""' from path in the property text file like this.
pathoffile=C:\Users\PFA Wongsawat\Desktop\part_no_and_path_list.txt
backuppath=C:\Users\PFA Wongsawat\Documents\part_no_and_path_list.txt
pdffolderpath=C:\Users\PFA Wongsawat\Downloads\
portname=COM3
The reason I can't see this because I debug the program by seeing the result in message box and it not match with the real one. Thank you.
I made an example program in order to get this going so I can actualy learn how to use it and aply it in my actual project.
In brief, my first try was writting the Person instance into the file, but I couldnt populate later a list with the different instances written (I only could see the first written, and the list had only 1 element). So I came up with saving the Person instance into a dictionary, writting the dictionary into the file, and then reading complete dictionary from file before adding a new element (Person instance), but I couldnt accomplish this either.
Let's say a I have a class Person
[Serializable]
public class Person
{
public string name, lname;
public int age;
public void newperson(string name,
string lname,
int age)
{
this.name = name;
this.lname = lname;
this.age = age;
}
}
in my main class I have two methods (honestly stolen from here, thanks #Daniel Schroeder #deadlydog) to write and read from file, through binary format.
Person nper = new Person(); //nper, an instance of Person
Dictionary<string, Person> dict = new Dictionary<string, Person>();
const string file = #"..\..\data.bin";
_
public static void WriteToBinaryFile<T>(string filePath, T objectToWrite, bool append = false)
{
using (Stream stream = File.Open(filePath, append ? FileMode.Append : FileMode.Create))
{
var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
binaryFormatter.Serialize(stream, objectToWrite);
}
}
_
public static T ReadFromBinaryFile<T>(string filePath)
{
using (Stream stream = File.Open(filePath, FileMode.Open))
{
var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
return (T)binaryFormatter.Deserialize(stream);
}
}
I kind of understand this methods, but, I would be unable to code/modify them, it exceeds my knowledge.
In order to test writting/reading I dragged a few textboxes and a button1
private void button1_Click(object sender, EventArgs e)
{
//saving textboxes to Person instance
nper.newperson(textBox4.Text, textBox5.Text, Convert.ToInt16(textBox6.Text));
//saving that object into a dictionary<string,Person> the key is the name, the object is the Person itself
dict[nper.name] = nper;
//Writting this dict
WriteToBinaryFile(file, dict, true);
}
Then, I dragged a few more separated textboxes to check the reading:
private void button2_Click(object sender, EventArgs e)
{
try
{
//read the file and save all into dict (is this working like I think it should? is dict, getting all the data from file?)
dict = ReadFromBinaryFile<Dictionary<string,Person>>(file);
//write into diferent textboxes the Person properties, for the key that matches with another text box that i fill manually to check
textBox1.Text = dict[tbSearch.Text].name;
textBox2.Text = dict[tbSearch.Text].lname;
textBox3.Text = dict[tbSearch.Text].age.ToString();
}
catch (Exception ex) { MessageBox.Show(ex.ToString()); }
in button2_Click is dict getting all the data from file?
edit: Try and result:
Let's say I fill the initial boxes with
John,
Doe,
40
click button1
then load another one,
Clark,
Kent,
50
If I write "John" in tbSearch I see full data of John (name, lastname and age)
If I fill with "Clark" I get a dictionary error "the key given was not present in the dictionary"
Set append parameter = false in WriteToBinaryFile(file, dict, false).
When first call of WriteToBinaryFile method executed BinaryFormatter write dictionary with one element, In second try write a dictionary with two element but appended to first write, So BinaryFormatter when try deserialize stream, read first section contains save dictionary with one key in first try (first button1 click).
I am writing an app in C# winForm, and I am using ListView to store some data.
I need to save this list of item when the form is closed and load it again when the form is opened again.
This is the code to add a new element on the list:
string[] timeitem = new string[2];
timeitem[0] = txtDescription.Text;
timeitem[1] = msktime.Text;
ListViewItem lvi = new ListViewItem(timeitem);
lstTimes.Items.Add(lvi);
What is the best way to save and load this list? I do not need a Dialog for the user, this list should be saved and loaded automatically each time the user open the form that contains the ListView item. I am open to use either .txt or xml file, whatever is the best/more easy to handle.
You could write a simple helper class for that:
class ListItemsHelper
{
private const string FILE_NAME = "items.dat";
public static void SaveData(Items items)
{
string data = SerializeItems(items);
File.WriteAllText(GetFilePath(), data);
}
public static Items LoadData()
{
string data = File.ReadAllText(GetFilePath());
return DeserializeItems(data);
}
private static string GetFilePath()
{
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, FILE_NAME);
}
private static string SerializeItems(Items items)
{
//Do serialization here
}
private static Items DeserializeItems(string data)
{
//Do deserialization here
}
}
Use:
ItemsStateHelper.SaveData(items);
Items data = ItemsStateHelper.LoadData();
Additionally, you would have to include some exception handling and choose where you want to save the file. In the code i posted it is saving on folder where the exe file is located.
I have a combobox which gets the list of items from the name of files I put together in one directory, the purpose for this is to make it dynamic - I'm very new to c# and it didn't occur to me a different way. - Here's the code for that bit:
string[] files = Directory.GetFiles(templatePath);
foreach (string file in files)
cbTemplates.Items.Add(System.IO.Path.GetFileNameWithoutExtension(file));
Basically, that works just fine, it populates my combobox with the names of the files I have in that path, the problem is that I need to open the file that's selected in the combobox and read its contents and place them in labels, I was thinking maybe StreamReader would help me here but I have NO clue on how to implement it, I've searched the internet but it looks like no one had the same idea before me. Can someone please point me in the right direction? A link to something similar or a guide of the objects I need to use would be great, thanks!
what you should do is store the names of the files in a single separate file (csv or xml). then use this file to both load the combobox and as an indexer.
for example lets say you have files a.txt, b.txt, and c.txt. you should (as you already are) read the file names programmatically THEN write them to a new file in whichever format you want, including a unique index scheme (numbers work fine).
your csv might look like this:
1, a.txt,
2, b.txt,
3, c.txt,
from here you can parse the newly created csv to your liking. Use it to populate your combobox, index being its value and filename its text. Then you can read your combobox selectedvalue, get the proper filename from the csv index, and finally open the file.
It may be longwinded but it'll work. You could also just use a multidimensional array, but this is more fun from an educational stand point, and it will help you with read/write operations.
It is not so easy to understand your problem. Do you want just to display filename w/o extension in your combobox? I hope this code will be usefull to you.
internal class FileDetail
{
public string Display { get; set; }
public string FullName { get; set; }
}
public partial class Example: Form // This is just widows form. InitializeComponent is implemented in separate file.
{
public Example()
{
InitializeComponent();
filesList.SelectionChangeCommitted += filesListSelectionChanged;
filesList.Click += filesListClick;
filesList.DisplayMember = "Display";
}
private void filesListClick(object sender, EventArgs e)
{
var dir = new DirectoryInfo(_baseDirectory);
filesList.Items.AddRange(
(from fi in dir.GetFiles()
select new FileDetail
{
Display = Path.GetFileNameWithoutExtension(fi.Name),
FullName = fi.FullName
}).ToArray()
);
}
private void filesListSelectionChanged(object sender, EventArgs e)
{
var text = File.ReadAllText(
(filesList.SelectedItem as FileDetail).FullName
);
fileContent.Text = text;
}
private static readonly string _baseDirectory = #"C:/Windows/System32/";
}
Thanks for all your help folks but I figured out how to get around my issue, I'll post the code for future incidents. pd. Sorry it took me this long to reply, I was on vacation
string[] fname = Directory.GetFiles(templatePath); // Gets all the file names from the path assigned to templatePath and assigns it to the string array fname
// Begin sorting through the file names assigned to the string array fname
foreach (string file in fname)
{
// Remove the extension from the file names and compare the list with the dropdown selected item
if (System.IO.Path.GetFileNameWithoutExtension(file) != cbTemplates.SelectedItem.ToString())
{
// StreamReader gets the contents from the found file and assigns them to the labels
using (var obj = new StreamReader(File.OpenRead(file)))
{
lbl1.Content = obj.ReadLine();
lbl2.Content = obj.ReadLine();
lbl3.Content = obj.ReadLine();
lbl4.Content = obj.ReadLine();
lbl5.Content = obj.ReadLine();
lbl6.Content = obj.ReadLine();
lbl7.Content = obj.ReadLine();
lbl8.Content = obj.ReadLine();
lbl9.Content = obj.ReadLine();
lbl10.Content = obj.ReadLine();
obj.Dispose();
}
}
}
I have a CSV file in this format:
"Call Type","Charge Type","Map to"
"51","","Mobile SMS"
"52","","Mobile SMS"
"DD","Local Calls","Local Calls"
"DD","National Calls","National Calls"
First two columns are the "source information" that my C# will insert, and the last column is what it will return.
Currently what I am doing is a switch statement hardcoded in c#.
var File001 = from line in File.ReadLines(bill_file)
let l = line.Split(',')
select new
{ CallType = ICD_map(l[5],l[3])}
where
l[5] = "51";
l[3] = "";
private static string ICD_map(string call_type_description, string call_category,)
{
case "51":
case "52":
return "Mobile SMS";
default:
return "Unknown";
}
I want this to be an expandable list thus my new method is to load the mapping table from a csv file. Can you suggest any improvements to this method to make my definition library expandable (hoping CSV file okay for this purpose, it is only 100 lines long so far, so not concerned about memory management).
What I have tried so far is:
class ICD_Map2
{
private string call_type;
private string charge_type;
private string map_to;
// Default constructor
public ICD_Map2() {
call_type = "Unknown";
charge_type = "Unknown";
map_to = "Unknown";
}
// Constructor
public ICD_Map2(string call_type, string charge_type, string map_to)
{
this.call_type = call_type;
this.charge_type = charge_type;
this.map_to = map_to;
}
}
List<ICD_Map2>maps = new List<ICD_Map2>();
private void button2_Click(object sender, EventArgs e)
{
// Start new thread to create BillSummary.csv
button1.Enabled = false;
maps.Clear();
//load mapping file
var reader = new StreamReader(File.OpenRead(#"Itemised_Call_Details_Map.csv"));
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
maps.Add(new ICD_Map2(values[0].Replace("\"",""), values[1].Replace("\"",""), values[2].Replace("\"","")));
textBox2.AppendText(Environment.NewLine + " Mapping: " + values[0].Replace("\"", "") + " to " + values[1].Replace("\"", ""));
}
I have loaded the CSV file to my program but I am unable to do the lookup from LINQ. Can you tell me the next process.
Open to any other method.
Thanks for your time.
I would suggest you to go with
http://www.codeproject.com/Articles/9258/A-Fast-CSV-Reader
It will give you lots of flexibility to play around with your code.
We have been using it in our projects, and it's really helpful to have full control inplace of writing generic CSV code which is prone to errors and bugs