.NET可以加载和解析与Java Properties类等效的属性文件吗?

时间:2009-01-27 22:36:17

标签: c# configuration file-io load

在C#中是否有一种简单的方法来读取属性文件,该文件在单独的行上有每个属性后跟一个等号和值,如下所示:

ServerName=prod-srv1
Port=8888
CustomProperty=Any value

在Java中,Properties类可以轻松处理此解析:

Properties myProperties=new Properties();
FileInputStream fis = new FileInputStream (new File("CustomProps.properties"));
myProperties.load(fis);
System.out.println(myProperties.getProperty("ServerName"));
System.out.println(myProperties.getProperty("CustomProperty"));

我可以轻松地在C#中加载文件并解析每一行,但是有没有内置的方法来轻松获取属性而无需解析密钥名称和等号本身?我发现的C#信息似乎总是支持XML,但这是一个我无法控制的现有文件,我宁愿将其保留为现有格式,因为需要更多时间让其他团队将其更改为XML而不是解析现有文件。

14 个答案:

答案 0 :(得分:39)

没有内置的支持。

你必须制作自己的“INIFileReader”。 也许是这样的?

var data = new Dictionary<string, string>();
foreach (var row in File.ReadAllLines(PATH_TO_FILE))
  data.Add(row.Split('=')[0], string.Join("=",row.Split('=').Skip(1).ToArray()));

Console.WriteLine(data["ServerName"]);

编辑:更新以反映保罗的评论。

答案 1 :(得分:18)

大多数Java“.properties”文件可以通过假设“=”是分隔符来拆分 - 但格式要复杂得多,并允许在属性名称中嵌入空格,等号,换行符和任何Unicode字符或价值。

我需要为C#应用程序加载一些Java属性,所以我已经使用与Java版本相同的方法实现了JavaProperties.cs以正确读取和写入“.properties”格式的文件 - 您可以在{{3}找到它}。

在那里,你会找到一个zip文件,其中包含该类的C#源代码以及我测试过的一些示例属性文件。

享受!

答案 2 :(得分:16)

最后一堂课。谢谢@eXXL

public class Properties
{
    private Dictionary<String, String> list;
    private String filename;

    public Properties(String file)
    {
        reload(file);
    }

    public String get(String field, String defValue)
    {
        return (get(field) == null) ? (defValue) : (get(field));
    }
    public String get(String field)
    {
        return (list.ContainsKey(field))?(list[field]):(null);
    }

    public void set(String field, Object value)
    {
        if (!list.ContainsKey(field))
            list.Add(field, value.ToString());
        else
            list[field] = value.ToString();
    }

    public void Save()
    {
        Save(this.filename);
    }

    public void Save(String filename)
    {
        this.filename = filename;

        if (!System.IO.File.Exists(filename))
            System.IO.File.Create(filename);

        System.IO.StreamWriter file = new System.IO.StreamWriter(filename);

        foreach(String prop in list.Keys.ToArray())
            if (!String.IsNullOrWhiteSpace(list[prop]))
                file.WriteLine(prop + "=" + list[prop]);

        file.Close();
    }

    public void reload()
    {
        reload(this.filename);
    }

    public void reload(String filename)
    {
        this.filename = filename;
        list = new Dictionary<String, String>();

        if (System.IO.File.Exists(filename))
            loadFromFile(filename);
        else
            System.IO.File.Create(filename);
    }

    private void loadFromFile(String file)
    {
        foreach (String line in System.IO.File.ReadAllLines(file))
        {
            if ((!String.IsNullOrEmpty(line)) &&
                (!line.StartsWith(";")) &&
                (!line.StartsWith("#")) &&
                (!line.StartsWith("'")) &&
                (line.Contains('=')))
            {
                int index = line.IndexOf('=');
                String key = line.Substring(0, index).Trim();
                String value = line.Substring(index + 1).Trim();

                if ((value.StartsWith("\"") && value.EndsWith("\"")) ||
                    (value.StartsWith("'") && value.EndsWith("'")))
                {
                    value = value.Substring(1, value.Length - 2);
                }

                try
                {
                    //ignore dublicates
                    list.Add(key, value);
                }
                catch { }
            }
        }
    }


}

样品使用:

//load
Properties config = new Properties(fileConfig);
//get value whith default value
com_port.Text = config.get("com_port", "1");
//set value
config.set("com_port", com_port.Text);
//save
config.Save()

答案 3 :(得分:7)

我写了一个方法,允许在文件中使用emty行,取消注释和引用。

示例:

var1 =“value1”
VAR2 = '值'

'var3 =取消评论
; var4 =也取消评论

以下是方法:

public static IDictionary ReadDictionaryFile(string fileName)
{
    Dictionary<string, string> dictionary = new Dictionary<string, string>();
    foreach (string line in File.ReadAllLines(fileName))
    {
        if ((!string.IsNullOrEmpty(line)) &&
            (!line.StartsWith(";")) &&
            (!line.StartsWith("#")) &&
            (!line.StartsWith("'")) &&
            (line.Contains('=')))
        {
            int index = line.IndexOf('=');
            string key = line.Substring(0, index).Trim();
            string value = line.Substring(index + 1).Trim();

            if ((value.StartsWith("\"") && value.EndsWith("\"")) ||
                (value.StartsWith("'") && value.EndsWith("'")))
            {
                value = value.Substring(1, value.Length - 2);
            }
            dictionary.Add(key, value);
        }
    }

    return dictionary;
}

答案 4 :(得分:5)

旧问题的另一个答案(2018年1月)(2009年1月)。

Java属性文件的规范在java.util.Properties.load(java.io.Reader)的JavaDoc中描述。一个问题是规范比我们可能有的第一印象有点复杂。另一个问题是,这里的一些答案随意添加了额外的规范 - 例如,;'被视为注释行的开头,但它们不应该。将删除属性值周围的双/单引号,但它们不应该是。

以下是要考虑的要点。

  1. 有两种行,自然行逻辑行
  2. 自然行由\n\r\r\n或流的结尾终止。
  3. 通过使用反斜杠字符\转义行终止符序列,逻辑行可以分布在几个相邻的自然行中。
  4. 逻辑行中第二行和后续自然行开头的任何空格都将被丢弃。
  5. 空格是空格(\u0020),标签(\t\u0009)和表单Feed \f\u000C )。
  6. 如说明书中明确指出的那样,&#34;仅检查行终止符序列之前的字符以确定行终止符是否被转义是不够的。必须有奇数个连续的反斜杠才能对行终止符进行转义。由于输入是从左到右处理的,因此在行终止符(或其他地方)之前,非零偶数的2n个连续反斜杠在转义处理后编码n个反斜杠。&#34;
  7. =用作键和值之间的分隔符。
  8. :也用作键和值之间的分隔符。
  9. 可以省略键和值之间的分隔符。
  10. 评论行的第一个非空格字符为#!,表示允许#!之前的前导空格。
  11. 注释行无法扩展到下一个自然行,即使其行终止符前面有\
  12. 正如规范中明确指出的那样,=:和空格可以嵌入到密钥中,如果它们被反斜杠转义。
  13. 使用\r\n转义序列可以包含偶数行终结符字符。
  14. 如果省略值,则使用空字符串作为值。
  15. \uxxxx用于表示Unicode字符。
  16. 无效转义字符前的反斜杠字符不会被视为错误;它被默默地放弃了。
  17. 因此,例如,如果test.properties具有以下内容:

    # A comment line that starts with '#'.
       # This is a comment line having leading white spaces.
    ! A comment line that starts with '!'.
    
    key1=value1
      key2 : value2
        key3 value3
    key\
      4=value\
        4
    \u006B\u0065\u00795=\u0076\u0061\u006c\u0075\u00655
    \k\e\y\6=\v\a\lu\e\6
    
    \:\ \= = \\colon\\space\\equal
    

    它应该被解释为以下键值对。

    +------+--------------------+
    | KEY  | VALUE              |
    +------+--------------------+
    | key1 | value1             |
    | key2 | value2             |
    | key3 | value3             |
    | key4 | value4             |
    | key5 | value5             |
    | key6 | value6             |
    | : =  | \colon\space\equal |
    +------+--------------------+
    
    Authlete.Authlete NuGet包中的

    PropertiesLoader类可以解释规范的格式。以下示例代码:

    using System;
    using System.IO;
    using System.Collections.Generic;
    using Authlete.Util;
    
    namespace MyApp
    {
        class Program
        {
            public static void Main(string[] args)
            {
                string file = "test.properties";
                IDictionary<string, string> properties;
    
                using (TextReader reader = new StreamReader(file))
                {
                    properties = PropertiesLoader.Load(reader);
                }
    
                foreach (var entry in properties)
                {
                    Console.WriteLine($"{entry.Key} = {entry.Value}");
                }
            }
        }
    }
    

    将生成此输出:

    key1 = value1
    key2 = value2
    key3 = value3
    key4 = value4
    key5 = value5
    key6 = value6
    : = = \colon\space\equal
    

    Java中的等效示例如下:

    import java.util.*;
    import java.io.*;
    
    public class Program
    {
        public static void main(String[] args) throws IOException
        {
            String file = "test.properties";
            Properties properties = new Properties();
    
            try (Reader reader = new FileReader(file))
            {
                 properties.load(reader);
            }
    
            for (Map.Entry<Object, Object> entry : properties.entrySet())
            {
                System.out.format("%s = %s\n", entry.getKey(), entry.getValue());
            }    
        }
    }
    

    源代码PropertiesLoader.cs可以在authlete-csharp中找到。 PropertiesLoader的{​​{3}}测试用xUnit编写。

答案 5 :(得分:2)

是的,我知道没有内置的课程可以做到这一点。

但这不应该是一个问题吗?通过将Stream.ReadToEnd()的结果存储在字符串中,基于新行分割然后在=字符上拆分每条记录,看起来很容易解析。你剩下的就是一堆关键值对,你可以很容易地把它们扔进字典里。

以下是一个可能适合您的示例:

public static Dictionary<string, string> GetProperties(string path)
{
    string fileData = "";
    using (StreamReader sr = new StreamReader(path))
    {
        fileData = sr.ReadToEnd().Replace("\r", "");
    }
    Dictionary<string, string> Properties = new Dictionary<string, string>();
    string[] kvp;
    string[] records = fileData.Split("\n".ToCharArray());
    foreach (string record in records)
    {
        kvp = record.Split("=".ToCharArray());
        Properties.Add(kvp[0], kvp[1]);
    }
    return Properties;
}

以下是如何使用它的示例:

Dictionary<string,string> Properties = GetProperties("data.txt");
Console.WriteLine("Hello: " + Properties["Hello"]);
Console.ReadKey();

答案 6 :(得分:2)

真正的答案是否定的(至少不是单独的)。您仍然可以编写自己的代码来执行此操作。

答案 7 :(得分:1)

C#通常使用基于xml的配置文件而不是像你所说的* .ini风格的文件,因此没有任何内置的处理它。但是,Google会返回number of promising results

答案 8 :(得分:1)

我不知道有任何内置方法可以做到这一点。但是,这样做似乎很容易,因为你必须担心的唯一分隔符是换行符和等号。

编写一个返回NameValueCollection的例程或给定文件内容的IDictionary非常容易。

答案 9 :(得分:1)

您还可以将C#自动属性语法与默认值和限制集一起使用。这里的优点是,您可以在属性“文件”中拥有任何类型的数据类型(现在实际上是一个类)。另一个优点是您可以使用C#属性语法来调用属性。但是,您只需要为每个属性添加几行(一个在属性声明中,一个在构造函数中)以使其工作。

using System;
namespace ReportTester {
   class TestProperties
   {
        internal String ReportServerUrl { get; private set; }
        internal TestProperties()
        {
            ReportServerUrl = "http://myhost/ReportServer/ReportExecution2005.asmx?wsdl";
        }
   }
}

答案 10 :(得分:1)

有几个NuGet包,但目前都处于预发布版本。

[更新] 截至2018年6月,Capgemini.Cauldron.Core.JavaProperties现在处于稳定版本(版本2.1.0和3.0.20)。

答案 11 :(得分:0)

我意识到这不是你所要求的,但以防万一:

如果要加载实际 Java属性文件,则需要适应其编码。 The Java docs表示编码是ISO 8859-1,其中包含一些您可能无法正确解释的转义序列。例如,查看this SO answer以查看将UTF-8转换为ISO 8859-1所需的内容(反之亦然)

当我们需要这样做时,我们找到了一个开源PropertyFile.cs并进行了一些更改以支持转义序列。这个类对于读/写场景来说是一个很好的类。您还需要支持PropertyFileIterator.cs课程。

即使你没有加载真正的Java属性,也要确保你的prop文件可以表达你需要保存的所有字符(至少是UTF-8)

答案 12 :(得分:0)

有你想要的确切解决方案。请查看here

中的文章

他的代码在效率方面有很多优点。

  1. 应用程序不会在每个请求中加载文本文件。它 仅将文本文件加载到内存中一次。对于随后的 请求,它直接从内存中返回值。这很多 如果您的文本文件包含数千或更多,则效率更高 键值对。
  2. 文本文件中的任何更改都不需要重新启动应用程序。一个 文件系统观察器已用于跟踪文件状态。 如果它发生变化,它会触发一个事件并加载新的变化 因此,对于内存,您可以将文本文件更改为一个 应用程序/文本编辑器,并在Web中查看已更改的效果 应用
  3. 您不仅可以在Web应用程序中使用它,还可以使用它 在任何桌面应用程序中使用它。
  4. 感谢。祝你有愉快的一天。

答案 13 :(得分:0)

没有没有:但我创建了一个简单的课程来帮助:

public class PropertiesUtility
{
    private static Hashtable ht = new Hashtable();
    public void loadProperties(string path)
    {
        string[] lines = System.IO.File.ReadAllLines(path);
        bool readFlag = false;
        foreach (string line in lines)
        {
            string text = Regex.Replace(line, @"\s+", "");
            readFlag =  checkSyntax(text);
            if (readFlag)
            {
                string[] splitText = text.Split('=');
                ht.Add(splitText[0].ToLower(), splitText[1]);
            }
        }
    }

    private bool checkSyntax(string line)
    {
        if (String.IsNullOrEmpty(line) || line[0].Equals('['))
        {
            return false;
        }

        if (line.Contains("=") && !String.IsNullOrEmpty(line.Split('=')[0]) && !String.IsNullOrEmpty(line.Split('=')[1]))
        {
            return true;
        }
        else
        {
            throw new Exception("Can not Parse Properties file please verify the syntax");
        }
    }

    public string getProperty(string key)
    {
        if (ht.Contains(key))
        {
            return ht[key].ToString();
        }
        else
        {
            throw new Exception("Property:" + key + "Does not exist");
        }

    }
}

希望这有帮助。