Lendo / gravando um arquivo INI


263

Existe alguma classe na estrutura .NET que possa ler / gravar arquivos .ini padrão:

[Section]
<keyname>=<value>
...

Delphi tem o TIniFilecomponente e eu quero saber se há algo semelhante para c #?


RemObjects possui uma biblioteca Delphi Prism chamada ShineOn que envia uma classe de arquivo INI semelhante. Mas você precisa do Delphi Prism para compilá-lo para .NET a partir da fonte, pois ainda não há um assembly compilado disponível. code.remobjects.com/p/shineon
Lex Li

1
Tem o mesmo problema e fiz a minha própria biblioteca para analisar arquivos ini: github.com/rickyah/ini-parser Espero que ajude
Ricardo Amores

5
Assim como Ricky, decidi fazer minha própria solução para isso. Está disponível em: github.com/MarioZ/MadMilkman.Ini
Mario Z

Respostas:


185

Os criadores da estrutura .NET desejam que você use arquivos de configuração baseados em XML, em vez de arquivos INI. Portanto, não, não há mecanismo interno para lê-los.

Existem soluções de terceiros disponíveis, no entanto.


24
Embora seja verdade que os arquivos de configuração XML sejam o caminho a seguir, isso ainda não é uma resposta para a pergunta ou é o VLQ apenas para link.
Danny Beckett

6
@aloneguid Eu diria que o grande conjunto de recursos disponíveis realmente contribuiu para os arquivos de configuração do .NET e acabou sendo um gigante estranho com muita magia neles. Eles se tornaram "código no arquivo de configuração" e isso gera muita complexidade, comportamentos estranhos e dificulta o gerenciamento de configurações. (Estou olhando para você, "provedores" de banco de dados e cadeias de conexão.) Portanto, os arquivos INI também são geralmente melhores para edição não manual também.
jpmc26

1
eu gosto do método antigo (P / Inovke) e você pode usar o unicode com o método antigo como este: File.WriteAllBytes (path, new byte [] {0xFF, 0xFE});
sailfish009

2
Bom pacote, mas poderia ser melhor. Ele não pode analisar um valor que contém '=' Ou '\ n' completamente
Ahmad Behjati

211

Prefácio

Em primeiro lugar, leia esta postagem do blog do MSDN sobre as limitações dos arquivos INI . Se atender às suas necessidades, continue lendo.

Esta é uma implementação concisa que escrevi, utilizando o Windows P / Invoke original, por isso é suportada por todas as versões do Windows com .NET instaladas (por exemplo, Windows 98 - Windows 10). Por meio deste, libero-o para o domínio público - você pode usá-lo comercialmente sem atribuição.

A pequena classe

Adicione uma nova classe chamada IniFile.csao seu projeto:

using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;

// Change this to match your program's normal namespace
namespace MyProg
{
    class IniFile   // revision 11
    {
        string Path;
        string EXE = Assembly.GetExecutingAssembly().GetName().Name;

        [DllImport("kernel32", CharSet = CharSet.Unicode)]
        static extern long WritePrivateProfileString(string Section, string Key, string Value, string FilePath);

        [DllImport("kernel32", CharSet = CharSet.Unicode)]
        static extern int GetPrivateProfileString(string Section, string Key, string Default, StringBuilder RetVal, int Size, string FilePath);

        public IniFile(string IniPath = null)
        {
            Path = new FileInfo(IniPath ?? EXE + ".ini").FullName;
        }

        public string Read(string Key, string Section = null)
        {
            var RetVal = new StringBuilder(255);
            GetPrivateProfileString(Section ?? EXE, Key, "", RetVal, 255, Path);
            return RetVal.ToString();
        }

        public void Write(string Key, string Value, string Section = null)
        {
            WritePrivateProfileString(Section ?? EXE, Key, Value, Path);
        }

        public void DeleteKey(string Key, string Section = null)
        {
            Write(Key, null, Section ?? EXE);
        }

        public void DeleteSection(string Section = null)
        {
            Write(null, null, Section ?? EXE);
        }

        public bool KeyExists(string Key, string Section = null)
        {
            return Read(Key, Section).Length > 0;
        }
    }
}

Como usá-lo

Abra o arquivo INI de uma das 3 maneiras a seguir:

// Creates or loads an INI file in the same directory as your executable
// named EXE.ini (where EXE is the name of your executable)
var MyIni = new IniFile();

// Or specify a specific name in the current dir
var MyIni = new IniFile("Settings.ini");

// Or specify a specific name in a specific dir
var MyIni = new IniFile(@"C:\Settings.ini");

Você pode escrever alguns valores assim:

MyIni.Write("DefaultVolume", "100");
MyIni.Write("HomePage", "http://www.google.com");

Para criar um arquivo como este:

[MyProg]
DefaultVolume=100
HomePage=http://www.google.com

Para ler os valores do arquivo INI:

var DefaultVolume = IniFile.Read("DefaultVolume");
var HomePage = IniFile.Read("HomePage");

Opcionalmente, você pode definir [Section]:

MyIni.Write("DefaultVolume", "100", "Audio");
MyIni.Write("HomePage", "http://www.google.com", "Web");

Para criar um arquivo como este:

[Audio]
DefaultVolume=100

[Web]
HomePage=http://www.google.com

Você também pode verificar a existência de uma chave como esta:

if(!MyIni.KeyExists("DefaultVolume", "Audio"))
{
    MyIni.Write("DefaultVolume", "100", "Audio");
}

Você pode excluir uma chave da seguinte maneira:

MyIni.DeleteKey("DefaultVolume", "Audio");

Você também pode excluir uma seção inteira (incluindo todas as chaves) da seguinte maneira:

MyIni.DeleteSection("Web");

Por favor, sinta-se livre para comentar com quaisquer melhorias!


4
Estou um pouco atrasado, mas falta o GetSections()método.
stil

2
Talvez um padrão mais tradicional seja o arquivo .ini por aplicativo (não por assembly) Path.GetFullPath(IniPath ?? Path.ChangeExtension(Application.ExecutablePath, ".ini")).
Eugene Ryabtsev

3
Muito bom ! Colocá-lo no github?
Emrys Myrooin

2
@danny Beckett, muito bem feito. Isso é quase exatamente igual ao que eu usei nos últimos anos do .Net. Atualizado do código antigo anos atrás.
Damian

10
Antigo agora, e por mais que eu respeite Raymond Chen, muitas das limitações desse artigo eram limitações da biblioteca INI específica no Windows, e não o formato INI em si. Outros, como permissões granulares, podem ser facilmente evitados por meio de vários arquivos. Uma biblioteca oficial e modernizada do INI seria muito bem-vinda até hoje.
Joel Coehoorn

68

Este artigo no CodeProject " Uma classe de manipulação de arquivos INI usando C # " deve ajudar.

O autor criou uma classe C # "Ini", que expõe duas funções do KERNEL32.dll. Essas funções são: WritePrivateProfileStringe GetPrivateProfileString. Você precisará de dois espaços para nome: System.Runtime.InteropServicese System.Text.

Etapas para usar a classe Ini

Na definição do namespace do seu projeto, inclua

using INI;

Crie um INIFile como este

INIFile ini = new INIFile("C:\\test.ini");

Use IniWriteValuepara escrever um novo valor em uma chave específica em uma seção ou use IniReadValuepara ler um valor de uma chave em uma Seção específica.

Nota: se você está começando do zero, pode ler este artigo do MSDN : Como: Adicionar arquivos de configuração de aplicativos a projetos C # . É uma maneira melhor de configurar seu aplicativo.


1
Eu quero ler o arquivo INI completo. Como a fazer o mesmo em vez de ler a seção, chave
venkat

isso funcionou para mim e depois parou de funcionar a partir de outro ponto. Nenhuma idéia ainda o que se passou diferente sob o capô
Nawfal

1
Cuidado com o uso dessas funções obsoletas da API do Win32. Mais informações: stackoverflow.com/questions/11451641/…
Pedro77

Eu usei essa abordagem por algum tempo, mas os aprimoramentos de segurança iniciados no Win7 praticamente acabaram com isso para mim. Você ainda pode usar essa abordagem, mas armazenará o .ini no ProgramData e o aplicativo lerá / gravará lá.
Jess

Não salve os arquivos ini de configuração do aplicativo no ProgramData. Eles não pertencem ao Registry ou ProgramData. Os arquivos de configuração devem estar nas pastas LocalApplicationData.
deegee

47

Eu encontrei esta implementação simples:

http://bytes.com/topic/net/insights/797169-reading-parsing-ini-file-c

Funciona bem para o que eu preciso.

Aqui está como você o usa:

public class TestParser
{
    public static void Main()
    {
        IniParser parser = new IniParser(@"C:\test.ini");

        String newMessage;

        newMessage = parser.GetSetting("appsettings", "msgpart1");
        newMessage += parser.GetSetting("appsettings", "msgpart2");
        newMessage += parser.GetSetting("punctuation", "ex");

        //Returns "Hello World!"
        Console.WriteLine(newMessage);
        Console.ReadLine();
    }
}

Aqui está o código:

using System;
using System.IO;
using System.Collections;

public class IniParser
{
    private Hashtable keyPairs = new Hashtable();
    private String iniFilePath;

    private struct SectionPair
    {
        public String Section;
        public String Key;
    }

    /// <summary>
    /// Opens the INI file at the given path and enumerates the values in the IniParser.
    /// </summary>
    /// <param name="iniPath">Full path to INI file.</param>
    public IniParser(String iniPath)
    {
        TextReader iniFile = null;
        String strLine = null;
        String currentRoot = null;
        String[] keyPair = null;

        iniFilePath = iniPath;

        if (File.Exists(iniPath))
        {
            try
            {
                iniFile = new StreamReader(iniPath);

                strLine = iniFile.ReadLine();

                while (strLine != null)
                {
                    strLine = strLine.Trim().ToUpper();

                    if (strLine != "")
                    {
                        if (strLine.StartsWith("[") && strLine.EndsWith("]"))
                        {
                            currentRoot = strLine.Substring(1, strLine.Length - 2);
                        }
                        else
                        {
                            keyPair = strLine.Split(new char[] { '=' }, 2);

                            SectionPair sectionPair;
                            String value = null;

                            if (currentRoot == null)
                                currentRoot = "ROOT";

                            sectionPair.Section = currentRoot;
                            sectionPair.Key = keyPair[0];

                            if (keyPair.Length > 1)
                                value = keyPair[1];

                            keyPairs.Add(sectionPair, value);
                        }
                    }

                    strLine = iniFile.ReadLine();
                }

            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (iniFile != null)
                    iniFile.Close();
            }
        }
        else
            throw new FileNotFoundException("Unable to locate " + iniPath);

    }

    /// <summary>
    /// Returns the value for the given section, key pair.
    /// </summary>
    /// <param name="sectionName">Section name.</param>
    /// <param name="settingName">Key name.</param>
    public String GetSetting(String sectionName, String settingName)
    {
        SectionPair sectionPair;
        sectionPair.Section = sectionName.ToUpper();
        sectionPair.Key = settingName.ToUpper();

        return (String)keyPairs[sectionPair];
    }

    /// <summary>
    /// Enumerates all lines for given section.
    /// </summary>
    /// <param name="sectionName">Section to enum.</param>
    public String[] EnumSection(String sectionName)
    {
        ArrayList tmpArray = new ArrayList();

        foreach (SectionPair pair in keyPairs.Keys)
        {
            if (pair.Section == sectionName.ToUpper())
                tmpArray.Add(pair.Key);
        }

        return (String[])tmpArray.ToArray(typeof(String));
    }

    /// <summary>
    /// Adds or replaces a setting to the table to be saved.
    /// </summary>
    /// <param name="sectionName">Section to add under.</param>
    /// <param name="settingName">Key name to add.</param>
    /// <param name="settingValue">Value of key.</param>
    public void AddSetting(String sectionName, String settingName, String settingValue)
    {
        SectionPair sectionPair;
        sectionPair.Section = sectionName.ToUpper();
        sectionPair.Key = settingName.ToUpper();

        if (keyPairs.ContainsKey(sectionPair))
            keyPairs.Remove(sectionPair);

        keyPairs.Add(sectionPair, settingValue);
    }

    /// <summary>
    /// Adds or replaces a setting to the table to be saved with a null value.
    /// </summary>
    /// <param name="sectionName">Section to add under.</param>
    /// <param name="settingName">Key name to add.</param>
    public void AddSetting(String sectionName, String settingName)
    {
        AddSetting(sectionName, settingName, null);
    }

    /// <summary>
    /// Remove a setting.
    /// </summary>
    /// <param name="sectionName">Section to add under.</param>
    /// <param name="settingName">Key name to add.</param>
    public void DeleteSetting(String sectionName, String settingName)
    {
        SectionPair sectionPair;
        sectionPair.Section = sectionName.ToUpper();
        sectionPair.Key = settingName.ToUpper();

        if (keyPairs.ContainsKey(sectionPair))
            keyPairs.Remove(sectionPair);
    }

    /// <summary>
    /// Save settings to new file.
    /// </summary>
    /// <param name="newFilePath">New file path.</param>
    public void SaveSettings(String newFilePath)
    {
        ArrayList sections = new ArrayList();
        String tmpValue = "";
        String strToSave = "";

        foreach (SectionPair sectionPair in keyPairs.Keys)
        {
            if (!sections.Contains(sectionPair.Section))
                sections.Add(sectionPair.Section);
        }

        foreach (String section in sections)
        {
            strToSave += ("[" + section + "]\r\n");

            foreach (SectionPair sectionPair in keyPairs.Keys)
            {
                if (sectionPair.Section == section)
                {
                    tmpValue = (String)keyPairs[sectionPair];

                    if (tmpValue != null)
                        tmpValue = "=" + tmpValue;

                    strToSave += (sectionPair.Key + tmpValue + "\r\n");
                }
            }

            strToSave += "\r\n";
        }

        try
        {
            TextWriter tw = new StreamWriter(newFilePath);
            tw.Write(strToSave);
            tw.Close();
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    /// <summary>
    /// Save settings back to ini file.
    /// </summary>
    public void SaveSettings()
    {
        SaveSettings(iniFilePath);
    }
}

38
+1 para compensar acima do voto negativo. Do que você realmente reclama? Ele disse que encontrou. Você o recusou por não encontrar um com acessadores genéricos e uso do construtor de strings?
Tormod 15/09

1
@ Tormod: Gostaria de poder rebaixar o comentário. É um fórum técnico quando votamos em soluções, não na intenção (obviamente positiva). Se uma solução publicada pelo próprio Knuth tivesse falhas, ela deveria - e deveria - ser apontada. Não importa se a solução foi encontrada ou escrita pelo pôster.
precisa

7
Eu acho que você estica a definição de "falha". Se a solução não enfatizar suas sensibilidades, simplesmente não faça votos positivos. Acabei de deixar uma nota dizendo que eu já neguei o voto negativo para que os outros 7 caras que votaram no meu comentário não fizessem isso sozinhos.
Tormod 21/02

21

O código na resposta do joerage é inspirador.

Infelizmente, ele altera as letras maiúsculas e minúsculas dos caracteres e não trata dos comentários. Então, eu escrevi algo que deveria ser robusto o suficiente para ler (apenas) arquivos INI muito sujos e permitir recuperar as chaves como estão.

Ele usa algum LINQ, um dicionário de strings que não faz distinção entre maiúsculas e minúsculas para armazenar seções, chaves e valores e ler o arquivo de uma só vez.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

class IniReader
{
    Dictionary<string, Dictionary<string, string>> ini = new Dictionary<string, Dictionary<string, string>>(StringComparer.InvariantCultureIgnoreCase);

    public IniReader(string file)
    {
        var txt = File.ReadAllText(file);

        Dictionary<string, string> currentSection = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);

        ini[""] = currentSection;

        foreach(var line in txt.Split(new[]{"\n"}, StringSplitOptions.RemoveEmptyEntries)
                               .Where(t => !string.IsNullOrWhiteSpace(t))
                               .Select(t => t.Trim()))
        {
            if (line.StartsWith(";"))
                continue;

            if (line.StartsWith("[") && line.EndsWith("]"))
            {
                currentSection = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
                ini[line.Substring(1, line.LastIndexOf("]") - 1)] = currentSection;
                continue;
            }

            var idx = line.IndexOf("=");
            if (idx == -1)
                currentSection[line] = "";
            else
                currentSection[line.Substring(0, idx)] = line.Substring(idx + 1);
        }
    }

    public string GetValue(string key)
    {
        return GetValue(key, "", "");
    }

    public string GetValue(string key, string section)
    {
        return GetValue(key, section, "");
    }

    public string GetValue(string key, string section, string @default)
    {
        if (!ini.ContainsKey(section))
            return @default;

        if (!ini[section].ContainsKey(key))
            return @default;

        return ini[section][key];
    }

    public string[] GetKeys(string section)
    {
        if (!ini.ContainsKey(section))
            return new string[0];

        return ini[section].Keys.ToArray();
    }

    public string[] GetSections()
    {
        return ini.Keys.Where(t => t != "").ToArray();
    }
}

4
e obrigado por não colocar isso catch (Exception ex) { throw ex; }lá #
Mark Schultheiss

1
Boa! Pelo menos algumas mudanças são necessárias para funcionar melhor. Linha 16: ini [""] = currentSection; Para: // ini [""] = currentSection; Isso deve ser removido, pois sempre que o primeiro elemento [0] for um segmento vazio devido a essa inicialização. Linha 36: currentSection [line.Substring (0, idx)] = line.Substring (idx + 1); Para: currentSection [line.Substring (0, idx) .Trim ()] = line.Substring (idx + 1) .Trim (); A chave e os valores devem ser aparados independentemente, não apenas na linha Aparar. Nos arquivos de configuração do INI, geralmente os que adicionam pares K-> V tendem a alinhar esses iguais dentro das seções. Obrigado!
LXSoft 4/18

Já faz um bom tempo. Muito obrigado por suas sugestões. Todos eles fazem sentido e merecem esse código para uma boa atualização.
Larry

13

Quero apresentar uma biblioteca IniParser que criei completamente em c #, para que não contenha dependências em nenhum sistema operacional, o que o torna compatível com Mono. Código aberto com licença MIT - para que possa ser usado em qualquer código.

Você pode verificar a fonte no GitHub , e também está disponível como um pacote NuGet

É altamente configurável e realmente simples de usar .

Desculpem o plugue descarado, mas espero que ajude quem estiver revisando esta resposta.


4

Se você precisar apenas de acesso de leitura e não de gravação, e estiver usando o Microsoft.Extensions.Confiuration(vem incluído por padrão no ASP.NET Core, mas também trabalha com programas regulares), você pode usar o pacote NuGet Microsoft.Extensions.Configuration.Inipara importar arquivos ini para as definições de configuração.

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddIniFile("SomeConfig.ini", optional: false);
    Configuration = builder.Build();
}

Só para acrescentar que, em seguida, obter as chaves comConfiguration["keyname"]
kofifus

@ Scott, o problema que estou tendo é por qualquer motivo que o IIS não o reconheça quando o aplicativo está sendo executado. está implantado e lá, mas não está sendo consumido. O HTTP 500.30 retornou e o log do IIS App diz "o arquivo de configuração não foi encontrado e não é opcional".
one.beat.consumer

3

Normalmente, quando você cria aplicativos usando C # e a estrutura .NET, não usa arquivos INI. É mais comum armazenar configurações em um arquivo de configuração baseado em XML ou no registro. No entanto, se o seu software compartilhar configurações com um aplicativo herdado, poderá ser mais fácil usar o arquivo de configuração, em vez de duplicar as informações em outro local.

A estrutura .NET não oferece suporte ao uso de arquivos INI diretamente. No entanto, você pode usar as funções da API do Windows com os Serviços de Invocação de Plataforma (P / Invoke) para gravar e ler os arquivos. Neste link, criamos uma classe que representa arquivos INI e usa funções da API do Windows para manipulá-los. Por favor, acesse o seguinte link.

Lendo e gravando arquivos INI


4
Fique fora do registro! Os dados de configuração do aplicativo não devem ser salvos no Registro.
deegee

3

Se você quer apenas um leitor simples, sem seções e quaisquer outras DLLs, aqui é uma solução simples:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Tool
{
    public class Config
    {
        Dictionary <string, string> values;
        public Config (string path)
        {
            values = File.ReadLines(path)
            .Where(line => (!String.IsNullOrWhiteSpace(line) && !line.StartsWith("#")))
            .Select(line => line.Split(new char[] { '=' }, 2, 0))
            .ToDictionary(parts => parts[0].Trim(), parts => parts.Length>1?parts[1].Trim():null);
        }
        public string Value (string name, string value=null)
        {
            if (values!=null && values.ContainsKey(name))
            {
                return values[name];
            }
            return value;
        }
    }
}

Amostra de uso:

    file = new Tool.Config (Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\config.ini");
    command = file.Value ("command");
    action = file.Value ("action");
    string value;
    //second parameter is default value if no key found with this name
    value = file.Value("debug","true");
    this.debug = (value.ToLower()=="true" || value== "1");
    value = file.Value("plain", "false");
    this.plain = (value.ToLower() == "true" || value == "1");

Enquanto isso, configure o conteúdo do arquivo (como você vê no símbolo # dos comentários da linha):

#command to run
command = php

#default script
action = index.php

#debug mode
#debug = true

#plain text mode
#plain = false

#icon = favico.ico

3

Tente este método:

public static Dictionary<string, string> ParseIniDataWithSections(string[] iniData)
{
    var dict = new Dictionary<string, string>();
    var rows = iniData.Where(t => 
        !String.IsNullOrEmpty(t.Trim()) && !t.StartsWith(";") && (t.Contains('[') || t.Contains('=')));
    if (rows == null || rows.Count() == 0) return dict;
    string section = "";
    foreach (string row in rows)
    {
        string rw = row.TrimStart();
        if (rw.StartsWith("["))
            section = rw.TrimStart('[').TrimEnd(']');
        else
        {
            int index = rw.IndexOf('=');
            dict[section + "-" + rw.Substring(0, index).Trim()] = rw.Substring(index+1).Trim().Trim('"');
        }
    }
    return dict;
}

Ele cria o dicionário onde a chave é "-". Você pode carregá-lo assim:

var dict = ParseIniDataWithSections(File.ReadAllLines(fileName));

3

PeanutButter.INI é uma classe empacotada em Nuget para manipulação de arquivos INI. Ele suporta leitura / gravação, incluindo comentários - seus comentários são preservados na gravação. Parece ser razoavelmente popular, testado e fácil de usar. Também é totalmente gratuito e de código aberto.

Disclaimer: Eu sou o autor de PeanutButter.INI.


Você poderia fornecer um link para a documentação do PeanutButter.INI?
Shroombaker 8/07/19


3

Estou atrasado para ingressar na festa, mas tive o mesmo problema hoje e escrevi a seguinte implementação:

using System.Text.RegularExpressions;

static bool match(this string str, string pat, out Match m) =>
    (m = Regex.Match(str, pat, RegexOptions.IgnoreCase)).Success;

static void Main()
{
    Dictionary<string, Dictionary<string, string>> ini = new Dictionary<string, Dictionary<string, string>>();
    string section = "";

    foreach (string line in File.ReadAllLines(.........)) // read from file
    {
        string ln = (line.Contains('#') ? line.Remove(line.IndexOf('#')) : line).Trim();

        if (ln.match(@"^[ \t]*\[(?<sec>[\w\-]+)\]", out Match m))
            section = m.Groups["sec"].ToString();
        else if (ln.match(@"^[ \t]*(?<prop>[\w\-]+)\=(?<val>.*)", out m))
        {
            if (!ini.ContainsKey(section))
                ini[section] = new Dictionary<string, string>();

            ini[section][m.Groups["prop"].ToString()] = m.Groups["val"].ToString();
        }
    }


    // access the ini file as follows:
    string content = ini["section"]["property"];
}

Deve-se notar que esta implementação não trata de seções ou propriedades que não são encontradas. Para conseguir isso, você deve estender a Dictionary<,>classe-para manipular chaves não encontradas.


Para serializar uma instância de Dictionary<string, Dictionary<string, string>>para um .iniarquivo, use o seguinte código:

string targetpath = .........;
Dictionary<string, Dictionary<string, string>> ini = ........;
StringBuilder sb = new StringBuilder();

foreach (string section in ini.Keys)
{
    sb.AppendLine($"[{section}]");

    foreach (string property in ini[section].Keys)
        sb.AppendLine($"{property}={ini[section][property]");
}

File.WriteAllText(targetpath, sb.ToString());

2

Há um Analisador Ini disponível no CommonLibrary.NET

Isso possui várias sobrecargas muito convenientes para obter seções / valores e é muito leve.


1
Caso não seja óbvio olhar para o nível superior da biblioteca (não era óbvio para mim!), A classe IniDcoument e outros estão no ComLib.IO.
Tim Keating

2
Para quem olha para essa rota, o CommonLibrary.NET parece não seguir as convenções .INI. Ele usa dois-pontos ":" como delimitador, em vez de sinal de igual, e não processa comentários (iniciar uma linha com um sinal de ponto-e-vírgula ou libra fará com que a análise falhe).
jmmr

2

Aqui está minha própria versão, usando expressões regulares. Esse código pressupõe que cada nome de seção seja exclusivo - se, no entanto, isso não for verdade - faz sentido substituir Dicionário por Lista. Esta função suporta comentários de arquivo .ini, iniciando em ';' personagem. Seção começa normalmente [seção], e pares de valores-chave também vêm normalmente "chave = valor". A mesma suposição das seções - o nome da chave é único.

/// <summary>
/// Loads .ini file into dictionary.
/// </summary>
public static Dictionary<String, Dictionary<String, String>> loadIni(String file)
{
    Dictionary<String, Dictionary<String, String>> d = new Dictionary<string, Dictionary<string, string>>();

    String ini = File.ReadAllText(file);

    // Remove comments, preserve linefeeds, if end-user needs to count line number.
    ini = Regex.Replace(ini, @"^\s*;.*$", "", RegexOptions.Multiline);

    // Pick up all lines from first section to another section
    foreach (Match m in Regex.Matches(ini, "(^|[\r\n])\\[([^\r\n]*)\\][\r\n]+(.*?)(\\[([^\r\n]*)\\][\r\n]+|$)", RegexOptions.Singleline))
    {
        String sectionName = m.Groups[2].Value;
        Dictionary<String, String> lines = new Dictionary<String, String>();

        // Pick up "key = value" kind of syntax.
        foreach (Match l in Regex.Matches(ini, @"^\s*(.*?)\s*=\s*(.*?)\s*$", RegexOptions.Multiline))
        {
            String key = l.Groups[1].Value;
            String value = l.Groups[2].Value;

            // Open up quotation if any.
            value = Regex.Replace(value, "^\"(.*)\"$", "$1");

            if (!lines.ContainsKey(key))
                lines[key] = value;
        }

        if (!d.ContainsKey(sectionName))
            d[sectionName] = lines;
    }

    return d;
}

Essa função não funciona, para mim: esquece uma seção em duas. Eu tentei com e sem linhas vazias antes de [Seção].
iksess

você pode copiar exemplo do seu. ini que não funciona?
TarmoPikaro

-3

Aqui está a minha turma, funciona como um encanto:

public static class IniFileManager
{


    [DllImport("kernel32")]
    private static extern long WritePrivateProfileString(string section,
        string key, string val, string filePath);
    [DllImport("kernel32")]
    private static extern int GetPrivateProfileString(string section,
             string key, string def, StringBuilder retVal,
        int size, string filePath);
    [DllImport("kernel32.dll")]
    private static extern int GetPrivateProfileSection(string lpAppName,
             byte[] lpszReturnBuffer, int nSize, string lpFileName);


    /// <summary>
    /// Write Data to the INI File
    /// </summary>
    /// <PARAM name="Section"></PARAM>
    /// Section name
    /// <PARAM name="Key"></PARAM>
    /// Key Name
    /// <PARAM name="Value"></PARAM>
    /// Value Name
    public static void IniWriteValue(string sPath,string Section, string Key, string Value)
    {
        WritePrivateProfileString(Section, Key, Value, sPath);
    }

    /// <summary>
    /// Read Data Value From the Ini File
    /// </summary>
    /// <PARAM name="Section"></PARAM>
    /// <PARAM name="Key"></PARAM>
    /// <PARAM name="Path"></PARAM>
    /// <returns></returns>
    public static string IniReadValue(string sPath,string Section, string Key)
    {
        StringBuilder temp = new StringBuilder(255);
        int i = GetPrivateProfileString(Section, Key, "", temp,
                                        255, sPath);
        return temp.ToString();

    }

}

O uso é óbvio, pois é uma classe estática, basta chamar IniFileManager.IniWriteValue para ler uma seção ou IniFileManager.IniReadValue para ler uma seção.


Essa abordagem já foi mostrada e explicada em outra resposta . O que sua resposta acrescenta que não é abordada por essa?
Palec

Observe que ele funciona apenas se o arquivo .ini estiver salvo no UNICODE (16bit LE). Use o Notepad ++ para converter o texto em unicode, porque se você o salvar em UTF-8 não funcionará. Também ANSI é aceitável, mas você não pode ler letras acentuadas
user2991288

-6

Você deve ler e gravar dados de arquivos xml, pois pode salvar um objeto inteiro em xml e também pode preencher um objeto a partir de um xml salvo. É melhor um fácil de manipular objetos.

Aqui está como fazer isso: Gravar dados do objeto em um arquivo XML: https://msdn.microsoft.com/en-us/library/ms172873.aspx Ler dados do objeto de um arquivo XML: https://msdn.microsoft. com / pt-br / library / ms172872.aspx


1
Os links para recursos externos são incentivados, mas adicione contexto ao redor do link para que seus colegas usuários tenham uma idéia do que é e por que está lá. Sempre cite a parte mais relevante de um link importante, caso o site de destino esteja inacessível ou fique permanentemente offline.
davejal 13/01

Acredito que os títulos dos links sejam muito claros sobre suas referências / contexto. Se você acha que isso não é suficiente, fique à vontade para editá-lo.
Daniel

1
Não aborda a questão real.
Erik Knowles
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.