Giter Club home page Giter Club logo

inifile-cpp's Introduction

inifile-cpp

License CMake codecov

inifile-cpp is a simple and easy to use single header-only ini file en- and decoder for C++.

Install

Install the headers using the CMake build system:

cd <path-to-repo>
mkdir build
cd build
cmake ..
make install

or simply copy the header file into your project and include it directly.

Usage

For examples on how to use and extend inifile-cpp for your custom needs, please have a look at the examples/ directory.

inifile-cpp allows loading data from any std::istream and requires a single function call or use the overloaded constructor.

#include <inicpp.h>

int main()
{
    // create istream object "is" ...

    // use function
    ini::IniFile myFirstIni;
    myFirstIni.decode(is);

    // or use the constructor
    ini::IniFile mySecondIni(is);
}

You can directly load ini-data from files by using the load() function. It requires a file path and automatically parses its contents:

#include <inicpp.h>

int main()
{
    // load an ini file
    ini::IniFile myIni;
    myIni.load("some/ini/path");
}

You can enable decoding of multi-line values using the setMultiLineValues(true) function. If you do this, field values may be continued on the next line, after indentation. Each line will be separated by the \n character in the final value, and the indentation will be removed.

#include <inicpp.h>

int main()
{
    // load an ini file
    ini::IniFile myIni;
    myIni.setMultiLineValues(true);
    myIni.load("some/ini/path");
}

When duplicate fields are decoded the previous value is simply overwritten by default. You can disallow duplicate fields from being overwritten by using the allowOverwriteDuplicateFields(false) function. If you do this, an exception will be thrown if a duplicate field is found inside a section.

#include <inicpp.h>

int main()
{
    // load an ini file
    ini::IniFile myIni;
    myIni.allowOverwriteDuplicateFields(false);
    // throws an exception if the ini file has duplicate fields
    myIni.load("some/ini/path");
}

Sections and fields can be accessed using the index operator []. The values can be converted to various native types:

bool myBool = myIni["Foo"]["myBool"].as<bool>();
char myChar = myIni["Foo"]["myChar"].as<char>();
unsigned char myUChar = myIni["Foo"]["myUChar"].as<unsigned char>();
int myInt = myIni["Foo"]["myInt"].as<int>();
unsigned int myUInt = myIni["Foo"]["myUInt"].as<unsigned int>();
long myLong = myIni["Foo"]["myLong"].as<long>();
unsigned long myULong = myIni["Foo"]["myULong"].as<unsigned long>();
float myFloat = myIni["Foo"]["myFloat"].as<float>();
double myDouble = myIni["Foo"]["myDouble"].as<double>();
std::string myStr = myIni["Foo"]["myStr"].as<std::string>();
const char *myStr2 = myIni["Foo"]["myStr"].as<const char*>();

Natively supported types are:

  • bool
  • char
  • unsigned char
  • short
  • unsigned short
  • int
  • unsigned int
  • long
  • unsigned long
  • float
  • double
  • std::string
  • const char *
  • std::string_view

Custom type conversions can be added by implementing specialized template of the ini::Convert<T> functor (see examples).

Values can be assigned to ini fileds just by using the assignment operator. The content of the inifile can then be written to any std::ostream object.

#include <inicpp.h>

int main()
{
    // create ostream object "os" ...

    ini::IniFile myIni;

    myIni["Foo"]["myInt"] = 1;
    myIni["Foo"]["myStr"] = "Hello world";
    myIni["Foo"]["myBool"] = true;
    myIni["Bar"]["myDouble"] = 1.2;

    myIni.encode(os);
}

You can directly save ini-data to files by using the save() function. It requires a file path and automatically stores the ini file contents:

#include <inicpp.h>

int main()
{
    ini::IniFile myIni;

    myIni["Foo"]["myInt"] = 1;
    myIni["Foo"]["myStr"] = "Hello world";
    myIni["Foo"]["myBool"] = true;
    myIni["Bar"]["myDouble"] = 1.2;

    myIni.save("some/ini/path");
}

You can define custom type conversions for inifile-cpp which will be automatically used by the assignment operator and the as() method of ini fields, e.g. you can add support for std::vector (see also examples):

// the conversion functor must live in the "ini" namespace
namespace ini
{
    /** Conversion functor to parse std::vectors from an ini field-
      * The generic template can be passed down to the vector. */
    template<typename T>
    struct Convert<std::vector<T>>
    {
        /** Decodes a std::vector from a string. */
        void decode(const std::string &value, std::vector<T> &result)
        {
            result.clear();

            // variable to store the decoded value of each element
            T decoded;
            // maintain a start and end pos within the string
            size_t startPos = 0;
            size_t endPos = 0;
            size_t cnt;

            while(endPos != std::string::npos)
            {
                if(endPos != 0)
                    startPos = endPos + 1;
                // search for the next comma as separator
                endPos = value.find(',', startPos);

                // if no comma was found use the rest of the string
                // as input
                if(endPos == std::string::npos)
                    cnt = value.size() - startPos;
                else
                    cnt = endPos - startPos;

                std::string tmp = value.substr(startPos, cnt);
                // use the conversion functor for the type contained in
                // the vector, so the vector can use any type that
                // is compatible with inifile-cpp
                Convert<T> conv;
                conv.decode(tmp, decoded);
                result.push_back(decoded);

            }
        }

        /** Encodes a std::vector to a string. */
        void encode(const std::vector<T> &value, std::string &result)
        {
            // variable to store the encoded element value
            std::string encoded;
            // string stream to build the result stream
            std::stringstream ss;
            for(size_t i = 0; i < value.size(); ++i)
            {
                // use the conversion functor for the type contained in
                // the vector, so the vector can use any type that
                // is compatible with inifile-cp
                Convert<T> conv;
                conv.encode(value[i], encoded);
                ss << encoded;

                // if this is not the last element add a comma as separator
                if(i != value.size() - 1)
                    ss << ',';
            }
            // store the created string in the result
            result = ss.str();
        }
    };
}

Contributing

If you want to contribute new features or bug fixes, simply file a pull request. Make sure all CI checks pass, otherwise PRs will not be merged.

License

inifile-cpp is licensed under the MIT license

inifile-cpp's People

Contributors

adrian104 avatar amelsmajic avatar capo80 avatar elrinor avatar jcastro0x avatar jgjunghein avatar jtikalsky avatar rjungheinrich avatar rookfighter avatar wirena avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

inifile-cpp's Issues

Disallow section duplication

I think that INI files with duplicated sections are confusing and error prone. For example:

[Foo]
var1 = hi
[Bar]
var2 = 12
[Foo]
var3 = 14

[Foo] will end up having both variables var1 and var3.
It is easy to spot this duplication in such a small example, but with bigger INI files this can be problematic.

I've made the pull request #14 to make the code throw an exception in this case.

Ignore non-significant spaces

Many INI files are edited directly by end users, and some of them want to use spaces to make the contents easy to read.

For example:

[SECT1]
  #This comment is indented, as is the following lines, which are also aligned
  John   = 1
  Evelyn = 2
  Lucas  = 3

I've made the pull request #11 to implement that.

By not existing keys returns 0

Hi, @Rookfighter. thank you for a great tool.

I'm trying to read an example.ini. And if the group-key-pair not exist in the ini-file, it returns "" that by converting to int gives 0 and by converting to bool gives false. It is in my opinion an unexpected behavior.

example.ini

[Group_1]
key_1 = 1;

main.cpp

#include <iostream>
#include "inicpp.h"
int main() {
	 ini::IniFile inifile;
	 inifile.load("example.ini");

	 int val_1 = inifile["Group_1"]["key_1"].as<int>();
	 int val_2 = inifile["Group_1"]["key_2"].as<int>();
	 
	 std::cout << "value 1 = " << val_1 << std::endl;
	 std::cout << "value 2 = " << val_2 << std::endl;
	 return 0;
}

Output:

value 1 = 1
value 2 = 0

My workaround for now is

#include <iostream>
#include <stdexcept>
#include "inicpp.h"

int readInt(ini::IniFile &inifile, const std::string &group, const std::string &key) {
	if (inifile[group][key].as<std::string>() == "")
		throw std::runtime_error(key + " is not found in " + group + "\n");
	else
		return inifile[group][key].as<int>();
}

int main() {
	ini::IniFile inifile;
	inifile.load("example.ini");

	try
	{
		int val_1 = readInt(inifile, "Group_1", "key_1");
		std::cout << "value 1 = " << val_1 << std::endl;
	} catch (...)
	{
		std::cout << "key_1 is not found in Group_1" << std::endl;
		inifile.clear();
		return 1;
	}

	try
	{
		int val_2 = readInt(inifile, "Group_1", "key_2");
		std::cout << "value 2 = " << val_2 << std::endl;
	} catch (...)
	{
		std::cout << "key_2 is not found in Group_1" << std::endl;
		inifile.clear();
		return 1;
	}

	return 0;
}

ini parsing failed, section not closed

I have a ini file:

[/home/xxx/.gnupg/]
Acls=user::rwx;group::---;other::---

[/home/xxx/.gnupg/.#lkxxx.29271]
Acls=user::rw-;group::r--;other::r--

[/home/xxx/.gnupg/openpgp-revocs.d/xxx.rev]
Acls=user::rw-;group::---;other::---

[/home/xxx/.gnupg/private-keys-v1.d/xxx.key]
Acls=user::rw-;group::---;other::---

[/home/xxx/.gnupg/private-keys-v1.d/xxx.key]
Acls=user::rw-;group::---;other::---

[/home/xxx/.gnupg/pubring.kbx]
Acls=user::rw-;group::r--;other::r--

[/home/xxx/.gnupg/pubring.kbx~]
Acls=user::rw-;group::---;other::---

[/home/xxx/.gnupg/trustdb.gpg]
Acls=user::rw-;group::---;other::---

[/home/xxx/.ssh]
Acls=user::rwx;group::r--;other::r--
[/home/xxx/.ssh/id_rsa]
Acls=user::rw-;group::---;other::---
[/home/xxx/.ssh/id_rsa.pem]
Acls=user::rw-;group::r--;other::r--
[/home/xxx/.ssh/id_rsa.pub]
Acls=user::rw-;group::r--;other::r--
[/home/xxx/.ssh/known_hosts]
Acls=user::rw-;group::---;other::---
[/home/xxx/.ssh/known_hosts.old]
Acls=user::rw-;group::r--;other::r--
[Defaults]
Excludes=
Includes=/home/xxx/.ssh;/home/xxx/.gnupg/;
SaveAcls=true

Then I get a error when I rerun my programe:

erminate called after throwing an instance of 'std::logic_error'
what(): l4: ini parsing failed, section not closed

But I can't find any error about this ini file :(

Why not

Вы можете использовать такой код (inicpp.h:234)?
void decode(const std::string &value, double &result) {
std::stringstream stream;
stream << value;
stream >> result;
}

Comments and line separators getting deleted while saving

@Rookfighter Thank you for this useful library.

I noticed that after updating the values and calling save option, all the comments and empty lines in the ini file gets removed.
Since most of these files are edited manually by users, retaining comments and white spaces are essential while saving.

Sample ini file

# Send a message to the world
[world]
message=Hello

# Just foo :)
[foo]
f1=123

Sample code

#include <inicpp.h>
int main()
{
    ini::IniFile myIni;
    myIni.load("some/ini/path");

    myIni["world"]["message"] = "Hello world";

    myIni.save("some/ini/path");
    
}

Result after saving the file

[world]
message=Hello world
[foo]
f1=123

Do let me know if there is already an option to avoid them being removed.

Restoring the master branch

Hello, my library figcone depends on inifile-cpp, which is downloaded during CMake configuration using FetchContent. As inifile-cpp doesn't provide any version tags, I specified the master branch to download it. Recently (?) the master branch was removed, and it rendered all versions of figcone broken unless the user disables the support of the ini format. By default, all config formats are supported, so the build is failing during configuration while trying to download inifile-cpp.

It will teach me to only use forked repositories for projects that don't have any versioning, but can you please restore the master branch to fix all previous releases of my library? I'm not asking to make it the main branch again, just to create it in the repo. There were no incompatible changes, so it can be set on the latest commit.

Disable sorting

How to disable ALPHABET sorting in output file?

It always broke my file structure...

For example...

[General]
NUM_AITEMS = 5
AITEMS_ENABLED = false
[AITEM1]
DATA = X

After save it make fully unreadabled:

[AITEM1]
DATA = X
[General]
AITEMS_ENABLED = false
NUM_AITEMS = 5

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.