Может ли .NET загрузить и проанализировать файл свойств, эквивалентный классу свойств Java? - PullRequest
52 голосов
/ 28 января 2009

Есть ли в 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 ]

39 голосов
/ 28 января 2009

Нет, встроенной поддержки для этого нет.

Вы должны сделать свой собственный 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"]);

Редактировать: Обновлено, чтобы отразить комментарий Павла.

18 голосов
/ 07 июня 2009

Большинство файлов Java ".properties" можно разделить, предполагая, что "=" является разделителем - но формат значительно более сложен, чем этот, и позволяет встраивать пробелы, равенства, символы новой строки и любые символы Юникода в любое имя свойства или значение.

Мне нужно было загрузить некоторые свойства Java для приложения C #, поэтому я реализовал JavaProperties.cs для правильного чтения и записи форматированных файлов «.properties», используя тот же подход, что и версия Java - вы можете найти его по адресу http://www.kajabity.com/index.php/2009/06/loading-java-properties-files-in-csharp/.

Там вы найдете zip-файл, содержащий исходный код C # для класса и некоторые примеры файлов свойств, с которыми я его тестировал.

Наслаждайтесь!

16 голосов
/ 08 октября 2011

Финальный класс. Спасибо @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()
7 голосов
/ 14 мая 2009

Я написал метод, который допускает пустые строки, комментарии и цитирование внутри файла.

Примеры:

var1 = "value1"
var2 = 'значение2'

'var3 = outcommented
; 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;
}
5 голосов
/ 03 января 2018

Еще один ответ (в январе 2018 г.) на старый вопрос (в январе 2009 г.).

Спецификация файла свойств Java описана в JavaDoc <a href="https://docs.oracle.com/javase/9/docs/api/java/util/Properties.html#load-java.io.Reader-" rel="noreferrer">java.util.Properties.load(java.io.Reader)</a>. Одна проблема заключается в том, что спецификация немного сложнее, чем первое впечатление, которое мы можем иметь. Другая проблема заключается в том, что в некоторые ответы здесь произвольно добавляются дополнительные спецификации - например, ; и ' рассматриваются как начало строк комментариев, но не должны. Двойные / одинарные кавычки вокруг значений свойств удаляются, но они не должны быть.

Следующие пункты необходимо учитывать.

  1. Существует два вида линий: натуральные линии и логические линии .
  2. Естественная линия заканчивается \n, \r, \r\n или концом потока.
  3. Логическая строка может быть разбросана по нескольким смежным естественным строкам, если экранировать последовательность символов конца строки с обратной косой чертой \.
  4. Любые пробелы в начале второй и последующих натуральных строк в логической строке отбрасываются.
  5. Пробелы - это пробел (, \u0020), табуляция (\t, \u0009) и подача формы (\f, \u000C).
  6. Как явно указано в спецификации, "недостаточно просто исследовать символ, предшествующий последовательности конца строки, чтобы решить, экранирован ли конец строки; должно быть нечетное количество непрерывных обратных косых черт для конца строки чтобы быть экранированным. Поскольку ввод обрабатывается слева направо, ненулевое четное число 2n непрерывных обратных косых черт перед разделителем строки (или в другом месте) кодирует n обратных косых черт после обработки escape. "
  7. = используется в качестве разделителя между ключом и значением.
  8. : также используется в качестве разделителя между ключом и значением.
  9. Разделитель между ключом и значением может быть опущен.
  10. Строка комментария имеет # или ! в качестве своих первых непробельных символов, что означает, что допускаются первые пробелы перед # или !.
  11. Строка комментария не может быть расширена до следующих натуральных строк, даже если ее символу конца строки предшествует \.
  12. Как прямо указано в спецификации, =, : и пробелы могут быть встроены в ключ, если они выходят из-за обратной косой черты.
  13. Даже символы конца строки могут быть включены с помощью escape-последовательностей \r и \n.
  14. Если значение опущено, в качестве значения используется пустая строка.
  15. \uxxxx используется для представления символа Unicode.
  16. Символ обратной косой черты перед недопустимым escape-символом не рассматривается как ошибка; он тихо сбрасывается.

Так, например, если 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 |
+------+--------------------+

PropertiesLoader класс в Authlete.Authlete Пакет NuGet может интерпретировать формат спецификации. Пример кода ниже:

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

Исходный код, <a href="https://github.com/authlete/authlete-csharp/blob/master/Authlete/Util/PropertiesLoader.cs" rel="noreferrer">PropertiesLoader.cs</a>, можно найти в authlete-csharp . xUnit тесты для PropertiesLoader записаны в <a href="https://github.com/authlete/authlete-csharp/blob/master/Authlete.Tests/Util/PropertiesLoaderTest.cs" rel="noreferrer">PropertiesLoaderTest.cs</a>.

2 голосов
/ 10 февраля 2012

Реальный ответ - нет (по крайней мере, сам по себе). Вы все еще можете написать свой собственный код для этого.

2 голосов
/ 28 января 2009

Да, для этого нет встроенных классов, о которых я знаю.

Но разве это не должно быть проблемой? Это выглядит достаточно просто для анализа, просто сохранив результат 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();
1 голос
/ 20 марта 2018

Для этого есть несколько пакетов NuGet, но все они находятся в предварительной версии.

[Update] По состоянию на июнь 2018 года Capgemini.Cauldron.Core.JavaProperties теперь в стабильной версии (версии 2.1.0 и 3.0.20).

1 голос
/ 12 сентября 2011

Вы также можете использовать автоматический синтаксис свойств C # со значениями по умолчанию и ограничительным набором. Преимущество здесь в том, что вы можете иметь любой тип данных в вашем файле свойств (теперь это фактически класс). Другое преимущество заключается в том, что вы можете использовать синтаксис свойства C # для вызова свойств. Тем не менее, вам нужно всего несколько строк для каждого свойства (одна в объявлении свойства и одна в конструкторе), чтобы эта работа работала.

using System;
namespace ReportTester {
   class TestProperties
   {
        internal String ReportServerUrl { get; private set; }
        internal TestProperties()
        {
            ReportServerUrl = "http://myhost/ReportServer/ReportExecution2005.asmx?wsdl";
        }
   }
}
1 голос
/ 28 января 2009

Я не знаю ни одного встроенного способа сделать это. Однако это может показаться достаточно простым, поскольку единственными разделителями, о которых вам нужно беспокоиться, являются символ новой строки и знак равенства.

Было бы очень легко написать процедуру, которая будет возвращать коллекцию NameValueCollection или IDictionary, учитывая содержимое файла.

...