How can I ban a variable from a list without removing it from that list by adding the variable to a list of "banned" variable?
I wish to be able to type in a string. That string is compared to the file names in a folder. If there is a match, the file is read. If I type this same string again, the file should not be read again. There for I want to have a list of "banned" string that is checked whilst typing to avoid the file to be read again.
I have tried a few ways but not getting there. Below is an example of my last attempt.
What would be the best way?
public class test
{
string scl= "test3";
List <string> lsf,lso;
void Start ()
{
lsf=//file names
new List<string>();
lso=//files open
new List<string>();
lsf.Add("test0");
lsf.Add("test1");
lsf.Add("test2");
lsf.Add("test3");
lsf.Add("test4");
lso.Add("idhtk49fngo");//random string
}
void Update ()
{
if
(
Input.GetKeyDown("a")
)
{
for
(
int i=0;
i<lsf.Count;
i++
)
{
if(lsf[i]==scl)
{
Debug.Log
(i+" is read");
for
(
int j=0;
j<lso.Count;
j++
)
{
//how can i avoid reading
//lsf[3] here the second time
//"a" is pressed (by having "test3"
//added to a "ban" list (lso) )
if(scl!=lso[j])
{
lso.Add(lsf[i]);
}
}
}
}
}
}
Michael’s answer is the way to go here but it can be improved using the more appropriate collection available to keep track of opened files; if you want uniqueness use a set, not a list:
HashSet<string> openedFiles = new HashSet<string>();
public static bool TryFirstRead(
string path,
out string result)
{
if (openedFiles.Add(path))
{
result = File.ReadAllText(path);
return true;
}
result = null;
return false;
}
Also, I’d avoid throwing vexing exceptions. Give the consumer a friendly way to know if the file was read or not, don’t make them end up having to use exceptions as a flow control mechanism.
I didn't understand although if you want to replace a value from another list.
You can use the list index to create a new list with the values which you removed.
String list1 = {"hi", "hello", "World"};
String list2 = {"bye", "goodbye", "World"};
List1[1] = list2[1];
I would suggest such way:
public static List<string> openedFiles = new List<string>();
public static string ReadFileAndAddToOpenedList(string path)
{
if (openedFiles.Contains(path))
throw new Exception("File already opened");
// Instead of throwing exception you could for example just log this or do something else, like:
// Consolle.WriteLine("File already opened");
else
{
openedFiles.Add(path);
return File.ReadAllText(path);
}
}
The idea is - on every file read, add file to list, so you can check every time you try read file, if it was already read (or opened). If it is, throw exception (or do something else). Else read a file.
You could instead of making it a string list use your own class
public class MyFile
{
public string Name;
public bool isOpen;
public MyFile(string name)
{
Name = name;
isOpen = false;
}
}
List<MyFile> lsf = new List<MyFile>()
{
new MyFile("test0"),
new MyFile("test1"),
new MyFile("test2"),
new MyFile("test3"),
new MyFile("test4")
};
Than when you read the file set isOpen to true
MyFile[someIndex].isOpen = true;
and later you can check this
// E.g. skip in a loop
if(MyFile[someIndex]) continue;
You could than also use Linq in order to get a list of only unread files:
var unreadFiles = lsf.Select(f => f.Name).Where(file => !file.isOpen);
Related
I am trying to find a way to creat class instances from a file and also to use that file as a way to give the class properties their values. I can manually put all the information in but it will be better to do it through a file so I can alter the file, which will alter the program for me.
Here is the code so far... When I run it, it says
class Program
{
class dish
{
public class starters { public string starter; public string alteration; }
static void Main(string[] args)
{
List<dish.starters> starter = new List<dish.starters>();
using (StreamReader reader = File.OpenText(#"D:\Visual Studio\Projects\Bella Italia\Food\Starters.txt"))
{
IDictionary<string, dish.starters> value = new Dictionary<string, dish.starters>();
while (!reader.EndOfStream)
{
value[reader.ReadLine()] = new dish.starters();
value[reader.ReadLine()].starter = reader.ReadLine();
}
foreach(var x in value.Values)
{
Console.WriteLine(x.starter);
}
}
Console.ReadLine();
}
}
}
When I try to run it, it says
Exception Unhandled
System.Collections.Generic.KeyNotFoundException: 'The given key was not present in the dictionary.'
You are reading two consecutive line here. The second line probably doesn't have an associated entry in the dictionary (and you don't want that duplication either):
value[reader.ReadLine() /*one*/] = new dish.starters();
value[reader.ReadLine() /*two*/].starter = reader.ReadLine();
Store the key in a variable and reuse that:
string key = reader.ReadLine();
value[key] = new dish.starters();
value[key].starter = reader.ReadLine();
Or create the object and assign later:
string key = reader.ReadLine();
var starters = new dish.starters();
starters.starter = reader.ReadLine()
value[key] = starters;
I am using c# and sitecore to basically use tokens in certain places ( see: how to create a custom token in sitecore ). I think I have a solution, but am not sure as to why it is not working, even though I am getting no errors.
Item tokenItem = Sitecore.Context.Database.Items["/sitecore/content/Site Content/Tokens"];
if (tokenItem.HasChildren)
{
var sValue = args.FieldValue.ToString();
foreach (Item child in tokenItem.Children)
{
if (child.Template.Name == "Token")
{
string home = child.Fields["Title"].Value;
string hContent = child.Fields["Content"].Value;
if (sValue.Contains(home))
{
home.Replace(home, hContent);
}
}
}
}
home and hContent pull up the correct values of each container, but when the page loads, it still has the "home" value inputted (the ie: ##sales) in the content area instead of the new value, which is stored in hContent. The sValue contains everything (tables, divs, text) and I was trying to single out a value that equals to "home" and replace the "home" value with hContent. What am I missing?
If your code is implemented as a processor for the RenderField pipeline, you need to put the result of your work back into args. Try something like this:
Item tokenItem = Sitecore.Context.Database.Items["/sitecore/content/Site Content/Tokens"];
if (tokenItem.HasChildren)
{
var sValue = args.Result.FirstPart;
foreach (Item child in tokenItem.Children){
if (child.Template.Name == "Token") {
string home = child.Fields["Title"].Value;
string hContent = child.Fields["Content"].Value;
if (sValue.Contains(home)) {
sValue = sValue.Replace(home, hContent);
}
}
}
args.Result.FirstPart = sValue;
}
Note that you need to be sure to patch this processor into the pipeline after the GetFieldValue processor. That processor is responsible for pulling the field value into args.Result.FirstPart.
You code isn't really doing anything. You seem to be replacing the tokens on the token item field itself (child.Fields["Title"] and child.Fields["Content"]), not on the output content stream.
Try the following, you need to set the args to the replaced value, replacing both the FirstPart and LastPart properties: Replace Tokens in Rich Text Fields Using the Sitecore ASP.NET CMS (link to the code in the "untested prototype" link).
I would refactor your code to make it easier:
public void Process(RenderFieldArgs args)
{
args.Result.FirstPart = this.Replace(args.Result.FirstPart);
args.Result.LastPart = this.Replace(args.Result.LastPart);
}
protected string Replace(string input)
{
Item tokenItem = Sitecore.Context.Database.Items["/sitecore/content/Site Content/Tokens"];
if (tokenItem.HasChildren)
{
foreach (Item child in tokenItem.Children)
{
if (child.Template.Name == "Token")
{
string home = child.Fields["Title"].Value;
string hContent = child.Fields["Content"].Value;
if (input.Contains(home))
{
return input.Replace(home, hContent);
}
}
}
}
return input;
}
This is still not optimal, but gets you closer.
Well, Do you know what happens when you performs home.Replace(home, hContent);, it will create a new instance by replacing the content of the come with what is in hContent so what you need to do is, assign this instance to a new variable or to home itself. hence the snippet will be like the following:
if (sValue.Contains(home))
{
home = home.Replace(home, hContent);
}
Have you tried:
home = home.Replace(home,hContent);
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 want to write an user defined error collection class which should collect all the Error's. When we validate an entity object if there is no error it should go and save to the Database. if Error there it should display it.
now i have wrote the class it collects the error and displays it successfully but when there is two identical error the class throws an exception.
(i use error-code for the error. the value for the error-code is in resx file from where the display method will take the value and display it. Display works perfectly)
//The code where it collects Error
if (objdepartment.Departmentname == null)
{
ErrorCollection.AddErrors("A1001","Department Name");
}
if (objdepartment.Departmentcode == null)
{
ErrorCollection.AddErrors("A1001","Department code");
}
//In the Errorcollection
public class ErrorCollection
{
static Dictionary<string,List<string>> ErrorCodes;
private ErrorCollection() { }
public static void AddErrors(string eCode,params string[] dataItem)
{
if (ErrorCodes == null)
{
ErrorCodes = new Dictionary<string, List<string>>();
}
List<String> lsDataItem = new List<String>();
foreach (string strD in dataItem)
lsDataItem.Add(strD);
ErrorCodes.Add(eCode, lsDataItem);
}
public static string DisplayErrors()
{
string ErrorMessage;
//string Key;
ErrorMessage = String.Empty;
if (ErrorCodes != null)
{
string Filepath= "D:\\Services\\ErrorCollection\\";
//Read Errors- Language Specsific message from resx file.
ResourceManager rm = ResourceManager.CreateFileBasedResourceManager("ErrorMessages", Filepath, null);
StringBuilder sb = new StringBuilder();
foreach (string error in ErrorCodes.Keys)
{
List<string> list = ErrorCodes[error];
if (error == "A0000")
{
sb.Append("System Exception : " + list[0]);
}
else
{
sb.Append(rm.GetString(error) + "\nBreak\n");
}
for (int counter = 0; counter < list.Count; counter++)
{
sb.Replace("{A}", list[counter]);
}
}
ErrorMessage = sb.ToString();
}
return ErrorMessage;
}
}
now when there is two common error. then the code shows an exception like "datakey already exist" in the line " ErrorCodes.Add(eCode, lsDataItem);" (the italic part where the exception throwed)
Well for one thing, having this statically is a terrible idea. You should create an instance of ErrorCollection to add the errors to IMO, and make the variable an instance variable instead of static.
Then you need to take a different approach within AddErrors, presumably adding all the new items if the key already exists. Something like this:
List<string> currentItems;
if (!ErrorCodes.TryGetValue(eCode, out currentItems))
{
currentItems = new List<string>);
ErrorCodes[eCode] = currentItems;
}
currentItems.AddRange(dataItem);
You are adding "A1001" twice as a key in a dictionary. That simply isn't allowed. However, more urgently - why is that dictionary static? That means that everything, anywhere, shares that error collection.
Suggestions:
make that not static (that is a bad idea - also, it isn't synchronized)
check for existence of the key, and react accordingly:
if(ErrorCodes.ContainsKey(eCode)) ErrorCodes[eCode].AddRange(lsDataItem);
else ErrorCodes.Add(eCode, lsDataItem);
As an aside, you might also consider implementing IDataErrorInfo, which is a built-in standard wrapper for this type of functionality, and will provide support for your error collection to work with a few standard APIs. But don't rush into this until you need it ;p