Domanda

What is the best way for storing a list of values for using in my program, and enable the user to change them? XML file or INI file and why?

If you have a better suggestion, you are welcome.

EDIT: For getting the most appropriate answer, here are some details. However, the answers can help other users as the custom details are written as comments only.

I have a collection of elements, each is marked with "true" or "false" value.
In my program I would like to use only the elements which are signed with "true" value in the list.

I can use an Enum with the "True" elements only, but I would like the user to choose his desired elements, by changing false to true and vice versa.

However, I do not want him to select the desired elements in every execution. Thats why I think about using a file that could be handy changed according to user preferance.

È stato utile?

Soluzione 2

In my opinion there are a lot of options, but when not very experienced you should keep it as easy as possible.

One way is to use the Settings you can generate with your project itself.

Just add a settings file to your project by clicking on Add > New Item in the Solution Explorer, and choose Settings File.

This is a useful article on adding lists to your settings file.


In your case you could also opt for a string setting. Just save the integer values to that string using:

Properties.Settings.Default.SettingX = string.Join(",", array);

Read them back using this:

string[] list = Properties.Settings.Default.SettingX.Split(',');

Altri suggerimenti

I suggest to use the .NET default way and this is for sure XML.

With XML you have some advantage over INI format:

  • Hierarchical data structure
  • Complex data types are easy to store (serialization)
  • You can simply validate with a xsd
  • XML is standardized and future proof

You should probably use the app.config configuration files.

It uses XML to store the settings, but the Framework takes care of most of the dirty work for you.

If you need to be able to change the setting from the application at runtime, have a look at Settings files.

Answer a question for best could be a huge deal. I think there are several good solutions here is mine, hope it helps.

Actually i work on a lot of apps which all of them need to be configured by end users. After a while spent using 'xml' i changed myself to simple ini file made only of "propertyName"="value".

I wrote a simple class to handle such those kind of files. The usage it's simple

Settings.ConfigProperties cfProps = new Settings.ConfigProperties("c:\\test.ini");
//if the file exists it will be read

//to update or insert new values
cfProps.UpdateValue("myNewProperty", "myValue");

//to get a stored value
String readValue = cfProps.ToString("myNewProperty");

//to permanently write changes to the ini file
cfProps.Save();

here is the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace Settings
{
    public class ConfigProperties
    {
        public class PropertyList<T> : List<ConfigProperty>
        {
            public ConfigProperty this[String propertyName]
            {
                get
                {
                    ConfigProperty ret = null;
                    foreach (ConfigProperty cp in this)
                    {
                        if (cp.Name == propertyName)
                        {
                            ret = cp;
                            break;
                        }
                    }
                    return ret;
                }
                set
                {
                    ConfigProperty ret = null;
                    foreach (ConfigProperty cp in this)
                    {
                        if (cp.Name == propertyName)
                        {
                            ret = cp;
                            break;
                        }
                    }
                    value = ret;
                }
            }
            public PropertyList()
                : base()
            {

            }

            public void Add(String Name, String Value)
            {
                ConfigProperty cp = new ConfigProperty();
                cp.Name = Name;
                cp.Value = Value;
                this.Add(cp);
            }
        }
        public class ConfigProperty
        {
            public String Name { get; set; }
            public String Value { get; set; }
            public ConfigProperty()
            {
                Name = "newPropertyName_" + DateTime.Now.Ticks.ToString();
                Value = "";
            }
            public ConfigProperty(String name, String value)
            {
                Name = name;
                Value = value;
            }
        }

        public String FileName { get; set; }
        public PropertyList<ConfigProperty> CFCollection { get; private set; }

        public ConfigProperties()
            : this(AppDomain.CurrentDomain.BaseDirectory + "config.ini")
        {
        }

        public ConfigProperties(String fileName)
        {
            CFCollection = new PropertyList<ConfigProperty>();
            FileName = fileName;
            if (fileName != null && File.Exists(fileName))
            {
                ReadFile();
            }
        }

        private void ReadFile()
        {
            if (File.Exists(FileName))
            {
                CFCollection = new PropertyList<ConfigProperty>();
                using (StreamReader sr = new StreamReader(FileName, Encoding.Default))
                {
                    while (!sr.EndOfStream)
                    {
                        String line = sr.ReadLine();
                        if (!line.StartsWith("#"))
                        {
                            ConfigProperty cf = new ConfigProperty();
                            String tmp = "";
                            Char splitter = '=';
                            for (int i = 0; i < line.Length; i++)
                            {
                                if (line[i] != splitter)
                                {
                                    tmp += ((Char)line[i]).ToString();
                                }
                                else
                                {
                                    cf.Name = tmp;
                                    tmp = "";
                                    splitter = '\n';
                                }
                            }
                            cf.Value = tmp;
                            if (cf.Name.Length > 0)
                            {
                                CFCollection.Add(cf);
                            }
                        }
                    }

                    sr.Close();
                }
            }
        }

        private void SaveConfigProperty(ConfigProperty prop)
        {
            List<String> output = new List<String>();
            if (File.Exists(FileName))
            {
                using (StreamReader sr = new StreamReader(FileName, Encoding.Default))
                {
                    while (!sr.EndOfStream)
                    {
                        String line = sr.ReadLine();
                        if (line.StartsWith(prop.Name + "="))
                        {
                            output.Add(prop.Name + "=" + prop.Value);
                        }
                        else
                        {
                            output.Add(line);
                        }
                    }
                    sr.Close();
                }
            }

            if (!output.Contains(prop.Name + "=" + prop.Value))
            {
                output.Add(prop.Name + "=" + prop.Value);
            }

            StreamWriter sw = new StreamWriter(FileName, false, Encoding.Default);

            foreach (String s in output)
            {
                sw.WriteLine(s);
            }
            sw.Close();
            sw.Dispose();
            GC.SuppressFinalize(sw);
            sw = null;

            output.Clear();
            output = null;

        }

        public void Save()
        {
            foreach (ConfigProperty cp in CFCollection)
            {
                SaveConfigProperty(cp);
            }
        }

        public void UpdateValue(String propertyName, String propertyValue)
        {
            try
            {
                IEnumerable<ConfigProperty> myProps = CFCollection.Where(cp => cp.Name.Equals(propertyName));
                if (myProps.Count() == 1)
                {
                    myProps.ElementAt(0).Value = propertyValue;
                }
                else if (myProps.Count() == 0)
                {
                    CFCollection.Add(new ConfigProperty(propertyName, propertyValue));
                }
                else
                {
                    throw new Exception("Can't find/set value for: " + propertyName);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public String ToString(String propertyName)
        {
            try
            {
                return CFCollection.Where(cp => cp.Name.Equals(propertyName)).First().Value;
            }
            catch (Exception ex)
            {
                throw new Exception("Can't find/read value for: " +
                    propertyName, ex);
            }
        }

    }
}

I would like to suggest JSON format

  • It can be read/edited by human
  • It supports hierarchy
  • It is not such complex like XML
  • It can be used like a charm (without ugly ini parsing)

formatted file looks like

{"DevCommentVisibility":27,"CommentVisibility":1,"IssueStatusAfterCheckin":4,"Log":{"Debug":false,"Info":true,"Warn":true,"FileName":"d:\temp\2.log"},"Language":"en"}

public static string Serialize(T pSettings)
{
    return (new JavaScriptSerializer()).Serialize(pSettings);
}

public static T Load(string fileName)
{
    T t = new T();
    if (File.Exists(fileName))
        try
        {
            t = (new JavaScriptSerializer()).Deserialize<T>(File.ReadAllText(fileName));
            var s = t as SettingsFoo;
            if (s != null)
                s.FileName = fileName;
        }
        catch (Exception ex)
        {
            Trace.WriteLine(string.Format("failed to parse settings file {0}", fileName));
            Trace.WriteLine(ex.Message);
        }
    else
    {
        Trace.WriteLine(string.Format("failed load settings '{0}' absolute '{1}'", fileName, Path.GetFullPath(fileName)));
        Save(t);
    }
    return t;
}

You can use a simple plain file.

Saving a List<int> list to a file.txt:

System.IO.File.WriteAllLines("file.txt", list.Select(v => v.ToString()).ToArray());

Loading:

List<int> loaded = System.IO.File.ReadAllLines("file.txt").Select(l => int.Parse(l)).ToList();

Use extension methods to design a nicer syntax and proper error handling

UPD: generic extension method I can propose (no exception handling):

public static class MyExtensions
{
    public static void SaveToFile<T>(this List<T> list, string filename)
    {
        System.IO.File.WriteAllLines(filename, list.Select(v => v.ToString()).ToArray());
    }

    public static void FillFromFile<T>(this List<T> list, string filename, Func<string, T> parser)
    {
        foreach (var line in System.IO.File.ReadAllLines(filename))
        {
            T item = parser(line);

            list.Add(item);
        }
    }
}

Use it like that:

List<int> list = new List<int>() { 0, 1, 2 };

list.SaveToFile("numbers.txt");

List<int> loaded = new List<int>();

loaded.FillFromFile("numbers.txt", (l) => int.Parse(l));
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top