文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

C++实现ini文件读写的示例代码

2024-04-02 19:55

关注

介绍

一般的ini配置文件由节、键、值组成。

【参数】(键=值),例如 :key=value;

【节】:所有的参数都是以节(section)为单位结合在一起的。所有的section名称都是独占一行,并且section名字都被方括号包围着([XXX])。在section声明后的所有parameters都属于该section。

例如:[section1]

所以一个包含节,键,值的简单ini配置文件,例如:

[port]
portName=port1
port=123

1.使用INIReader.h头文件

1.INIReader.h

// Read an INI file into easy-to-access name/value pairs.

// inih and INIReader are released under the New BSD license (see LICENSE.txt).
// Go to the project home page for more info:
//
// https://github.com/benhoyt/inih


#ifndef __INI_H__
#define __INI_H__


#ifdef __cplusplus
extern "C" {
#endif

#include <stdio.h>


typedef int (*ini_handler)(void* user, const char* section,
                           const char* name, const char* value);


typedef char* (*ini_reader)(char* str, int num, void* stream);


int ini_parse(const char* filename, ini_handler handler, void* user);


int ini_parse_file(FILE* file, ini_handler handler, void* user);


int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
                     void* user);


#ifndef INI_ALLOW_MULTILINE
#define INI_ALLOW_MULTILINE 1
#endif


#ifndef INI_ALLOW_BOM
#define INI_ALLOW_BOM 1
#endif


#ifndef INI_ALLOW_INLINE_COMMENTS
#define INI_ALLOW_INLINE_COMMENTS 1
#endif
#ifndef INI_INLINE_COMMENT_PREFIXES
#define INI_INLINE_COMMENT_PREFIXES ";"
#endif


#ifndef INI_USE_STACK
#define INI_USE_STACK 1
#endif


#ifndef INI_STOP_ON_FIRST_ERROR
#define INI_STOP_ON_FIRST_ERROR 0
#endif


#ifndef INI_MAX_LINE
#define INI_MAX_LINE 200
#endif

#ifdef __cplusplus
}
#endif



#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif

#include <stdio.h>
#include <ctype.h>
#include <string.h>

#if !INI_USE_STACK
#include <stdlib.h>
#endif

#define MAX_SECTION 50
#define MAX_NAME 50


inline static char* rstrip(char* s)
{
    char* p = s + strlen(s);
    while (p > s && isspace((unsigned char)(*--p)))
        *p = '\0';
    return s;
}


inline static char* lskip(const char* s)
{
    while (*s && isspace((unsigned char)(*s)))
        s++;
    return (char*)s;
}


inline static char* find_chars_or_comment(const char* s, const char* chars)
{
#if INI_ALLOW_INLINE_COMMENTS
    int was_space = 0;
    while (*s && (!chars || !strchr(chars, *s)) &&
           !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
        was_space = isspace((unsigned char)(*s));
        s++;
    }
#else
    while (*s && (!chars || !strchr(chars, *s))) {
        s++;
    }
#endif
    return (char*)s;
}


inline static char* strncpy0(char* dest, const char* src, size_t size)
{
    strncpy(dest, src, size);
    dest[size - 1] = '\0';
    return dest;
}


inline int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
                     void* user)
{
    
#if INI_USE_STACK
    char line[INI_MAX_LINE];
#else
    char* line;
#endif
    char section[MAX_SECTION] = "";
    char prev_name[MAX_NAME] = "";

    char* start;
    char* end;
    char* name;
    char* value;
    int lineno = 0;
    int error = 0;

#if !INI_USE_STACK
    line = (char*)malloc(INI_MAX_LINE);
    if (!line) {
        return -2;
    }
#endif

    
    while (reader(line, INI_MAX_LINE, stream) != NULL) {
        lineno++;

        start = line;
#if INI_ALLOW_BOM
        if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
                           (unsigned char)start[1] == 0xBB &&
                           (unsigned char)start[2] == 0xBF) {
            start += 3;
        }
#endif
        start = lskip(rstrip(start));

        if (*start == ';' || *start == '#') {
            
        }
#if INI_ALLOW_MULTILINE
        else if (*prev_name && *start && start > line) {

#if INI_ALLOW_INLINE_COMMENTS
        end = find_chars_or_comment(start, NULL);
        if (*end)
            *end = '\0';
        rstrip(start);
#endif

            
            if (!handler(user, section, prev_name, start) && !error)
                error = lineno;
        }
#endif
        else if (*start == '[') {
            
            end = find_chars_or_comment(start + 1, "]");
            if (*end == ']') {
                *end = '\0';
                strncpy0(section, start + 1, sizeof(section));
                *prev_name = '\0';
            }
            else if (!error) {
                
                error = lineno;
            }
        }
        else if (*start) {
            
            end = find_chars_or_comment(start, "=:");
            if (*end == '=' || *end == ':') {
                *end = '\0';
                name = rstrip(start);
                value = lskip(end + 1);
#if INI_ALLOW_INLINE_COMMENTS
                end = find_chars_or_comment(value, NULL);
                if (*end)
                    *end = '\0';
#endif
                rstrip(value);

                
                strncpy0(prev_name, name, sizeof(prev_name));
                if (!handler(user, section, name, value) && !error)
                    error = lineno;
            }
            else if (!error) {
                
                error = lineno;
            }
        }

#if INI_STOP_ON_FIRST_ERROR
        if (error)
            break;
#endif
    }

#if !INI_USE_STACK
    free(line);
#endif

    return error;
}


inline int ini_parse_file(FILE* file, ini_handler handler, void* user)
{
    return ini_parse_stream((ini_reader)fgets, file, handler, user);
}


inline int ini_parse(const char* filename, ini_handler handler, void* user)
{
    FILE* file;
    int error;

    file = fopen(filename, "r");
    if (!file)
        return -1;
    error = ini_parse_file(file, handler, user);
    fclose(file);
    return error;
}

#endif 


#ifndef __INIREADER_H__
#define __INIREADER_H__

#include <map>
#include <set>
#include <string>

// Read an INI file into easy-to-access name/value pairs. (Note that I've gone
// for simplicity here rather than speed, but it should be pretty decent.)
class INIReader
{
public:
    // Empty Constructor
    INIReader() {};

    // Construct INIReader and parse given filename. See ini.h for more info
    // about the parsing.
    explicit INIReader(const std::string& filename);

    // Construct INIReader and parse given file. See ini.h for more info
    // about the parsing.
    explicit INIReader(FILE *file);

    // Return the result of ini_parse(), i.e., 0 on success, line number of
    // first error on parse error, or -1 on file open error.
    int ParseError() const;

    // Return the list of sections found in ini file
    const std::set<std::string>& Sections() const;

    // Get a string value from INI file, returning default_value if not found.
    std::string Get(const std::string& section, const std::string& name,
                    const std::string& default_value) const;

    // Get an integer (long) value from INI file, returning default_value if
    // not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2").
    long GetInteger(const std::string& section, const std::string& name, long default_value) const;

    // Get a real (floating point double) value from INI file, returning
    // default_value if not found or not a valid floating point value
    // according to strtod().
    double GetReal(const std::string& section, const std::string& name, double default_value) const;

    // Get a single precision floating point number value from INI file, returning
    // default_value if not found or not a valid floating point value
    // according to strtof().
    float GetFloat(const std::string& section, const std::string& name, float default_value) const;
  
    // Get a boolean value from INI file, returning default_value if not found or if
    // not a valid true/false value. Valid true values are "true", "yes", "on", "1",
    // and valid false values are "false", "no", "off", "0" (not case sensitive).
    bool GetBoolean(const std::string& section, const std::string& name, bool default_value) const;

protected:
    int _error;
    std::map<std::string, std::string> _values;
    std::set<std::string> _sections;
    static std::string MakeKey(const std::string& section, const std::string& name);
    static int ValueHandler(void* user, const char* section, const char* name,
                            const char* value);
};

#endif  // __INIREADER_H__


#ifndef __INIREADER__
#define __INIREADER__

#include <algorithm>
#include <cctype>
#include <cstdlib>

inline INIReader::INIReader(const std::string& filename)
{
    _error = ini_parse(filename.c_str(), ValueHandler, this);
}

inline INIReader::INIReader(FILE *file)
{
    _error = ini_parse_file(file, ValueHandler, this);
}

inline int INIReader::ParseError() const
{
    return _error;
}

inline const std::set<std::string>& INIReader::Sections() const
{
    return _sections;
}

inline std::string INIReader::Get(const std::string& section, const std::string& name, const std::string& default_value) const
{
    std::string key = MakeKey(section, name);
    return _values.count(key) ? _values.at(key) : default_value;
}

inline long INIReader::GetInteger(const std::string& section, const std::string& name, long default_value) const
{
    std::string valstr = Get(section, name, "");
    const char* value = valstr.c_str();
    char* end;
    // This parses "1234" (decimal) and also "0x4D2" (hex)
    long n = strtol(value, &end, 0);
    return end > value ? n : default_value;
}

inline double INIReader::GetReal(const std::string& section, const std::string& name, double default_value) const
{
    std::string valstr = Get(section, name, "");
    const char* value = valstr.c_str();
    char* end;
    double n = strtod(value, &end);
    return end > value ? n : default_value;
}

inline float INIReader::GetFloat(const std::string& section, const std::string& name, float default_value) const
{
    std::string valstr = Get(section, name, "");
    const char* value = valstr.c_str();
    char* end;
    float n = strtof(value, &end);
    return end > value ? n : default_value;
}

inline bool INIReader::GetBoolean(const std::string& section, const std::string& name, bool default_value) const
{
    std::string valstr = Get(section, name, "");
    // Convert to lower case to make string comparisons case-insensitive
    std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower);
    if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1")
        return true;
    else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0")
        return false;
    else
        return default_value;
}

inline std::string INIReader::MakeKey(const std::string& section, const std::string& name)
{
    std::string key = section + "=" + name;
    // Convert to lower case to make section/name lookups case-insensitive
    std::transform(key.begin(), key.end(), key.begin(), ::tolower);
    return key;
}

inline int INIReader::ValueHandler(void* user, const char* section, const char* name,
                            const char* value)
{
    INIReader* reader = (INIReader*)user;
    std::string key = MakeKey(section, name);
    if (reader->_values[key].size() > 0)
        reader->_values[key] += "\n";
    reader->_values[key] += value;
    reader->_sections.insert(section);
    return 1;
}

#endif  // __INIREADER__

2.test.ini

3.INIReaderTest.cpp

// Example that shows simple usage of the INIReader class

#include <iostream>
#include <sstream>
#include "INIReader.h"

std::string sections(INIReader &reader)
{
    std::stringstream ss;
    std::set<std::string> sections = reader.Sections();
    for (std::set<std::string>::iterator it = sections.begin(); it != sections.end(); ++it)
        ss << *it << ",";
    return ss.str();
}

int main()
{
    INIReader reader("test.ini");

    if (reader.ParseError() < 0) {
        std::cout << "Can't load 'test.ini'\n";
        return 1;
    }
    std::cout << "Config loaded from 'test.ini': found sections=" << sections(reader)
              << " version="
              << reader.GetInteger("protocol", "version", -1) << ", name="
              << reader.Get("user", "name", "UNKNOWN") << ", email="
              << reader.Get("user", "email", "UNKNOWN") << ", multi="
              << reader.Get("user", "multi", "UNKNOWN") << ", pi="
              << reader.GetReal("user", "pi", -1) << ", active="
              << reader.GetBoolean("user", "active", true) << "\n";
    return 0;
}

2.使用ini.h头文件

现代 c++ 的另一个 .ini 解析器(为 cpp17 制作),灵感来自 inih 并进行了扩展。

1.ini.h



#ifndef __INI_H__
#define __INI_H__

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>

#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <iterator>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <vector>

namespace inih {


typedef int (*ini_handler)(void* user, const char* section, const char* name,
                           const char* value);


typedef char* (*ini_reader)(char* str, int num, void* stream);

#define INI_STOP_ON_FIRST_ERROR 1
#define INI_MAX_LINE 2000
#define INI_INITIAL_ALLOC 200
#define MAX_SECTION 50
#define MAX_NAME 50
#define INI_START_COMMENT_PREFIXES ";#"
#define INI_INLINE_COMMENT_PREFIXES ";"


inline static char* rstrip(char* s) {
    char* p = s + strlen(s);
    while (p > s && isspace((unsigned char)(*--p))) *p = '\0';
    return s;
}


inline static char* lskip(const char* s) {
    while (*s && isspace((unsigned char)(*s))) s++;
    return (char*)s;
}


inline static char* find_chars_or_comment(const char* s, const char* chars) {
    int was_space = 0;
    while (*s && (!chars || !strchr(chars, *s)) &&
           !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
        was_space = isspace((unsigned char)(*s));
        s++;
    }
    return (char*)s;
}


inline static char* strncpy0(char* dest, const char* src, size_t size) {
    strncpy(dest, src, size - 1);
    dest[size - 1] = '\0';
    return dest;
}


inline int ini_parse_stream(ini_reader reader, void* stream,
                            ini_handler handler, void* user) {
    
    char* line;
    size_t max_line = INI_INITIAL_ALLOC;
    char* new_line;
    size_t offset;
    char section[MAX_SECTION] = "";
    char prev_name[MAX_NAME] = "";

    char* start;
    char* end;
    char* name;
    char* value;
    int lineno = 0;
    int error = 0;

    line = (char*)malloc(INI_INITIAL_ALLOC);
    if (!line) {
        return -2;
    }

#if INI_HANDLER_LINENO
#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
#else
#define HANDLER(u, s, n, v) handler(u, s, n, v)
#endif

    
    while (reader(line, (int)max_line, stream) != NULL) {
        offset = strlen(line);
        while (offset == max_line - 1 && line[offset - 1] != '\n') {
            max_line *= 2;
            if (max_line > INI_MAX_LINE) max_line = INI_MAX_LINE;
            new_line = (char*)realloc(line, max_line);
            if (!new_line) {
                free(line);
                return -2;
            }
            line = new_line;
            if (reader(line + offset, (int)(max_line - offset), stream) == NULL)
                break;
            if (max_line >= INI_MAX_LINE) break;
            offset += strlen(line + offset);
        }

        lineno++;

        start = line;
        if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
            (unsigned char)start[1] == 0xBB &&
            (unsigned char)start[2] == 0xBF) {
            start += 3;
        }
        start = lskip(rstrip(start));

        if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
            
        } else if (*start == '[') {
            
            end = find_chars_or_comment(start + 1, "]");
            if (*end == ']') {
                *end = '\0';
                strncpy0(section, start + 1, sizeof(section));
                *prev_name = '\0';
            } else if (!error) {
                
                error = lineno;
            }
        } else if (*start) {
            
            end = find_chars_or_comment(start, "=:");
            if (*end == '=' || *end == ':') {
                *end = '\0';
                name = rstrip(start);
                value = end + 1;
                end = find_chars_or_comment(value, NULL);
                if (*end) *end = '\0';
                value = lskip(value);
                rstrip(value);

                
                strncpy0(prev_name, name, sizeof(prev_name));
                if (!HANDLER(user, section, name, value) && !error)
                    error = lineno;
            } else if (!error) {
                
                error = lineno;
            }
        }

        if (error) break;
    }

    free(line);

    return error;
}

inline int ini_parse_file(FILE* file, ini_handler handler, void* user) {
    return ini_parse_stream((ini_reader)fgets, file, handler, user);
}

inline int ini_parse(const char* filename, ini_handler handler, void* user) {
    FILE* file;
    int error;

    file = fopen(filename, "r");
    if (!file) return -1;
    error = ini_parse_file(file, handler, user);
    fclose(file);
    return error;
}

#endif 

#ifndef __INIREADER_H__
#define __INIREADER_H__

// Read an INI file into easy-to-access name/value pairs. (Note that I've gone
// for simplicity here rather than speed, but it should be pretty decent.)
class INIReader {
   public:
    // Empty Constructor
    INIReader(){};

    // Construct INIReader and parse given filename. See ini.h for more info
    // about the parsing.
    INIReader(std::string filename);

    // Construct INIReader and parse given file. See ini.h for more info
    // about the parsing.
    INIReader(FILE* file);

    // Return the result of ini_parse(), i.e., 0 on success, line number of
    // first error on parse error, or -1 on file open error.
    int ParseError() const;

    // Return the list of sections found in ini file
    const std::set<std::string> Sections() const;

    // Return the list of keys in the given section
    const std::set<std::string> Keys(std::string section) const;

    const std::unordered_map<std::string, std::string> Get(
        std::string section) const;

    template <typename T = std::string>
    T Get(const std::string& section, const std::string& name) const;

    template <typename T>
    T Get(const std::string& section, const std::string& name,
          T&& default_v) const;

    template <typename T = std::string>
    std::vector<T> GetVector(const std::string& section,
                             const std::string& name) const;

    template <typename T>
    std::vector<T> GetVector(const std::string& section,
                             const std::string& name,
                             const std::vector<T>& default_v) const;

    template <typename T = std::string>
    void InsertEntry(const std::string& section, const std::string& name,
                     const T& v);

    template <typename T = std::string>
    void InsertEntry(const std::string& section, const std::string& name,
                     const std::vector<T>& vs);

    template <typename T = std::string>
    void UpdateEntry(const std::string& section, const std::string& name,
                     const T& v);

    template <typename T = std::string>
    void UpdateEntry(const std::string& section, const std::string& name,
                     const std::vector<T>& vs);

   protected:
    int _error;
    std::unordered_map<std::string,
                       std::unordered_map<std::string, std::string>>
        _values;
    static int ValueHandler(void* user, const char* section, const char* name,
                            const char* value);

    template <typename T>
    T Converter(const std::string& s) const;

    const bool BoolConverter(std::string s) const;

    template <typename T>
    std::string V2String(const T& v) const;

    template <typename T>
    std::string Vec2String(const std::vector<T>& v) const;
};

#endif  // __INIREADER_H__

#ifndef __INIREADER__
#define __INIREADER__

inline INIReader::INIReader(std::string filename) {
    _error = ini_parse(filename.c_str(), ValueHandler, this);
    ParseError();
}

inline INIReader::INIReader(FILE* file) {
    _error = ini_parse_file(file, ValueHandler, this);
    ParseError();
}

inline int INIReader::ParseError() const {
    switch (_error) {
        case 0:
            break;
        case -1:
            throw std::runtime_error("ini file not found.");
        case -2:
            throw std::runtime_error("memory alloc error");
        default:
            throw std::runtime_error("parse error on line no: " +
                                     std::to_string(_error));
    }
    return 0;
}


inline const std::set<std::string> INIReader::Sections() const {
    std::set<std::string> retval;
    for (auto const& element : _values) {
        retval.insert(element.first);
    }
    return retval;
}


inline const std::set<std::string> INIReader::Keys(std::string section) const {
    auto const _section = Get(section);
    std::set<std::string> retval;
    for (auto const& element : _section) {
        retval.insert(element.first);
    }
    return retval;
}

inline const std::unordered_map<std::string, std::string> INIReader::Get(
    std::string section) const {
    auto const _section = _values.find(section);
    if (_section == _values.end()) {
        throw std::runtime_error("section '" + section + "' not found.");
    }
    return _section->second;
}


template <typename T>
inline T INIReader::Get(const std::string& section,
                        const std::string& name) const {
    auto const _section = Get(section);
    auto const _value = _section.find(name);

    if (_value == _section.end()) {
        throw std::runtime_error("key '" + name + "' not found in section '" +
                                 section + "'.");
    }

    std::string value = _value->second;

    if constexpr (std::is_same<T, std::string>()) {
        return value;
    } else if constexpr (std::is_same<T, bool>()) {
        return BoolConverter(value);
    } else {
        return Converter<T>(value);
    };
}


template <typename T>
inline T INIReader::Get(const std::string& section, const std::string& name,
                        T&& default_v) const {
    try {
        return Get<T>(section, name);
    } catch (std::runtime_error& e) {
        return default_v;
    }
}


template <typename T>
inline std::vector<T> INIReader::GetVector(const std::string& section,
                                           const std::string& name) const {
    std::string value = Get(section, name);

    std::istringstream out{value};
    const std::vector<std::string> strs{std::istream_iterator<std::string>{out},
                                        std::istream_iterator<std::string>()};
    try {
        std::vector<T> vs{};
        for (const std::string& s : strs) {
            vs.emplace_back(Converter<T>(s));
        }
        return vs;
    } catch (std::exception& e) {
        throw std::runtime_error("cannot parse value " + value +
                                 " to vector<T>.");
    }
}


template <typename T>
inline std::vector<T> INIReader::GetVector(
    const std::string& section, const std::string& name,
    const std::vector<T>& default_v) const {
    try {
        return GetVector<T>(section, name);
    } catch (std::runtime_error& e) {
        return default_v;
    };
}

template <typename T>
inline void INIReader::InsertEntry(const std::string& section,
                                   const std::string& name, const T& v) {
    if (_values[section][name].size() > 0) {
        throw std::runtime_error("duplicate key '" + std::string(name) +
                                 "' in section '" + section + "'.");
    }
    _values[section][name] = V2String(v);
}

template <typename T>
inline void INIReader::InsertEntry(const std::string& section,
                                   const std::string& name,
                                   const std::vector<T>& vs) {
    if (_values[section][name].size() > 0) {
        throw std::runtime_error("duplicate key '" + std::string(name) +
                                 "' in section '" + section + "'.");
    }
    _values[section][name] = Vec2String(vs);
}

template <typename T>
inline void INIReader::UpdateEntry(const std::string& section,
                                   const std::string& name, const T& v) {
    if (!_values[section][name].size()) {
        throw std::runtime_error("key '" + std::string(name) +
                                 "' not exist in section '" + section + "'.");
    }
    _values[section][name] = V2String(v);
}

template <typename T>
inline void INIReader::UpdateEntry(const std::string& section,
                                   const std::string& name,
                                   const std::vector<T>& vs) {
    if (!_values[section][name].size()) {
        throw std::runtime_error("key '" + std::string(name) +
                                 "' not exist in section '" + section + "'.");
    }
    _values[section][name] = Vec2String(vs);
}

template <typename T>
inline std::string INIReader::V2String(const T& v) const {
    std::stringstream ss;
    ss << v;
    return ss.str();
}

template <typename T>
inline std::string INIReader::Vec2String(const std::vector<T>& v) const {
    if (v.empty()) {
        return "";
    }
    std::ostringstream oss;
    std::copy(v.begin(), v.end() - 1, std::ostream_iterator<T>(oss, " "));
    oss << v.back();

    return oss.str();
}

template <typename T>
inline T INIReader::Converter(const std::string& s) const {
    try {
        T v{};
        std::istringstream _{s};
        _.exceptions(std::ios::failbit);
        _ >> v;
        return v;
    } catch (std::exception& e) {
        throw std::runtime_error("cannot parse value '" + s + "' to type<T>.");
    };
}

inline const bool INIReader::BoolConverter(std::string s) const {
    std::transform(s.begin(), s.end(), s.begin(), ::tolower);
    static const std::unordered_map<std::string, bool> s2b{
        {"1", true},  {"true", true},   {"yes", true}, {"on", true},
        {"0", false}, {"false", false}, {"no", false}, {"off", false},
    };
    auto const value = s2b.find(s);
    if (value == s2b.end()) {
        throw std::runtime_error("'" + s + "' is not a valid boolean value.");
    }
    return value->second;
}

inline int INIReader::ValueHandler(void* user, const char* section,
                                   const char* name, const char* value) {
    INIReader* reader = (INIReader*)user;
    if (reader->_values[section][name].size() > 0) {
        throw std::runtime_error("duplicate key '" + std::string(name) +
                                 "' in section '" + section + "'.");
    }
    reader->_values[section][name] = value;
    return 1;
}
#endif  // __INIREADER__

#ifndef __INIWRITER_H__
#define __INIWRITER_H__

class INIWriter {
   public:
    INIWriter(){};
    inline static void write(const std::string& filepath,
                             const INIReader& reader) {
        if (struct stat buf; stat(filepath.c_str(), &buf) == 0) {
            throw std::runtime_error("file: " + filepath + " already exist.");
        }
        std::ofstream out;
        out.open(filepath);
        if (!out.is_open()) {
            throw std::runtime_error("cannot open output file: " + filepath);
        }
        for (const auto& section : reader.Sections()) {
            out << "[" << section << "]\n";
            for (const auto& key : reader.Keys(section)) {
                out << key << "=" << reader.Get(section, key) << "\n";
            }
        }
        out.close();
    }
};
}
#endif 

2.config.ini

3.example.cpp

#include <iostream>
#include <string>
#include <typeinfo>
#include <boost/core/demangle.hpp>

#include "ini/ini.h"
using namespace inih;

namespace bc = boost::core;

int main() {
    INIReader r{"./test/fixtures/config.ini"};

    const auto& v1 = r.Get<std::string>("section1", "any");
    const auto& v2 = r.Get<int>("section1", "any");
    const auto& v3 = r.Get<double>("section1", "any");
    const auto& v4 = r.GetVector<float>("section2", "any_vec");
    const auto& v5{r.GetVector<std::string>("section2", "any_vec")};

    

    std::cout << "v1 = " << v1 << ", which is type: " << bc::demangle(typeid(v1).name()) << std::endl;
    std::cout << "v2 = " << v2 << ", which is type: " << bc::demangle(typeid(v2).name()) << std::endl;
    std::cout << "v3 = " << v3 << ", which is type: " << bc::demangle(typeid(v3).name()) << std::endl;
    std::cout << "v4 = "; for (auto& v : v4) std::cout << v << " "; std::cout << ", which is type: " << bc::demangle(typeid(v4).name()) << std::endl;
    std::cout << "v5 = "; for (auto& v : v5) std::cout << v << " "; std::cout << ", which is type: " << bc::demangle(typeid(v5).name()) << std::endl;

    // section exist, key not exist
    r.InsertEntry("section1", "my_custom_key", "hello world");

    // section&key not exist
    r.InsertEntry("new_section", "key1", 5);

    // Dump ini to file
    INIWriter::write("output.ini", r);

    return 0;
}

3.使用inipp.h头文件

3.1 解析算法

section被设置为空字符串

从文件中读取每一行并从空白处修剪。

3.2 默认section算法

将默认section中的每个变量插入到其他每个section中,而不覆盖现有变量。

3.3 Interpolation算法

1.每个section内部,遇到的每个variable都被{section:variable}代替

2.每个{section:variable}都被它的值代替

3.重复上一步,直到无法再进行替换,或者直到达到递归深度(默认为 10)。

3.4 代码实现

1.inipp.h



#pragma once

#include <algorithm>
#include <cctype>
#include <cstring>
#include <functional>
#include <iostream>
#include <list>
#include <vector>
#include <locale>
#include <map>
#include <memory>
#include <sstream>
#include <string>

namespace inipp {

namespace detail {

// trim functions based on http://stackoverflow.com/a/217605

template <class CharT>
inline void ltrim(std::basic_string<CharT> & s, const std::locale & loc) {
	s.erase(s.begin(),
                std::find_if(s.begin(), s.end(),
                             [&loc](CharT ch) { return !std::isspace(ch, loc); }));
}

template <class CharT>
inline void rtrim(std::basic_string<CharT> & s, const std::locale & loc) {
	s.erase(std::find_if(s.rbegin(), s.rend(),
                             [&loc](CharT ch) { return !std::isspace(ch, loc); }).base(),
                s.end());
}

template <class CharT, class UnaryPredicate>
inline void rtrim2(std::basic_string<CharT>& s, UnaryPredicate pred) {
	s.erase(std::find_if(s.begin(), s.end(), pred), s.end());
}

// string replacement function based on http://stackoverflow.com/a/3418285

template <class CharT>
inline bool replace(std::basic_string<CharT> & str, const std::basic_string<CharT> & from, const std::basic_string<CharT> & to) {
	auto changed = false;
	size_t start_pos = 0;
	while ((start_pos = str.find(from, start_pos)) != std::basic_string<CharT>::npos) {
		str.replace(start_pos, from.length(), to);
		start_pos += to.length();
		changed = true;
	}
	return changed;
}

} // namespace detail

template <typename CharT, typename T>
inline bool extract(const std::basic_string<CharT> & value, T & dst) {
	CharT c;
	std::basic_istringstream<CharT> is{ value };
	T result;
	if ((is >> std::boolalpha >> result) && !(is >> c)) {
		dst = result;
		return true;
	}
	else {
		return false;
	}
}

template <typename CharT>
inline bool extract(const std::basic_string<CharT> & value, std::basic_string<CharT> & dst) {
	dst = value;
	return true;
}

template <typename CharT, typename T>
inline bool get_value(const std::map<std::basic_string<CharT>, std::basic_string<CharT>> & sec, const std::basic_string<CharT> & key, T & dst) {
	const auto it = sec.find(key);
	if (it == sec.end()) return false;
	return extract(it->second, dst);
}

template <typename CharT, typename T>
inline bool get_value(const std::map<std::basic_string<CharT>, std::basic_string<CharT>>& sec, const CharT* key, T& dst) {
	return get_value(sec, std::basic_string<CharT>(key), dst);
}

template<class CharT>
class Format
{
public:
	// used for generating
	const CharT char_section_start;
	const CharT char_section_end;
	const CharT char_assign;
	const CharT char_comment;

	// used for parsing
	virtual bool is_section_start(CharT ch) const { return ch == char_section_start; }
	virtual bool is_section_end(CharT ch) const { return ch == char_section_end; }
	virtual bool is_assign(CharT ch) const { return ch == char_assign; }
	virtual bool is_comment(CharT ch) const { return ch == char_comment; }

	// used for interpolation
	const CharT char_interpol;
	const CharT char_interpol_start;
	const CharT char_interpol_sep;
	const CharT char_interpol_end;

	Format(CharT section_start, CharT section_end, CharT assign, CharT comment, CharT interpol, CharT interpol_start, CharT interpol_sep, CharT interpol_end)
		: char_section_start(section_start)
		, char_section_end(section_end)
		, char_assign(assign)
		, char_comment(comment)
		, char_interpol(interpol)
		, char_interpol_start(interpol_start)
		, char_interpol_sep(interpol_sep)
		, char_interpol_end(interpol_end) {}

	Format() : Format('[', ']', '=', ';', '$', '{', ':', '}') {}

	const std::basic_string<CharT> local_symbol(const std::basic_string<CharT>& name) const {
		return char_interpol + (char_interpol_start + name + char_interpol_end);
	}

	const std::basic_string<CharT> global_symbol(const std::basic_string<CharT>& sec_name, const std::basic_string<CharT>& name) const {
		return local_symbol(sec_name + char_interpol_sep + name);
	}
};

template<class CharT>
class Ini
{
public:
	using String = std::basic_string<CharT>;
	using Section = std::map<String, String>;
	using Sections = std::map<String, Section>;

	Sections sections;
	std::list<String> errors;
	std::shared_ptr<Format<CharT>> format;

	static const int max_interpolation_depth = 10;

	Ini() : format(std::make_shared<Format<CharT>>()) {};
	Ini(std::shared_ptr<Format<CharT>> fmt) : format(fmt) {};

	void generate(std::basic_ostream<CharT>& os) const {
		for (auto const & sec : sections) {
			os << format->char_section_start << sec.first << format->char_section_end << std::endl;
			for (auto const & val : sec.second) {
				os << val.first << format->char_assign << val.second << std::endl;
			}
			os << std::endl;
		}
	}

	void parse(std::basic_istream<CharT> & is) {
		String line;
		String section;
		const std::locale loc{"C"};
		while (std::getline(is, line)) {
			detail::ltrim(line, loc);
			detail::rtrim(line, loc);
			const auto length = line.length();
			if (length > 0) {
				const auto pos = std::find_if(line.begin(), line.end(), [this](CharT ch) { return format->is_assign(ch); });
				const auto & front = line.front();
				if (format->is_comment(front)) {
				}
				else if (format->is_section_start(front)) {
					if (format->is_section_end(line.back()))
						section = line.substr(1, length - 2);
					else
						errors.push_back(line);
				}
				else if (pos != line.begin() && pos != line.end()) {
					String variable(line.begin(), pos);
					String value(pos + 1, line.end());
					detail::rtrim(variable, loc);
					detail::ltrim(value, loc);
					auto & sec = sections[section];
					if (sec.find(variable) == sec.end())
						sec.emplace(variable, value);
					else
						errors.push_back(line);
				}
				else {
					errors.push_back(line);
				}
			}
		}
	}

	void interpolate() {
		int global_iteration = 0;
		auto changed = false;
		// replace each "variable"by"{section:variable}"
		for (auto & sec : sections)
			replace_symbols(local_symbols(sec.first, sec.second), sec.second);
		// replace each "${section:variable}" by its value
		do {
			changed = false;
			const auto syms = global_symbols();
			for (auto & sec : sections)
				changed |= replace_symbols(syms, sec.second);
		} while (changed && (max_interpolation_depth > global_iteration++));
	}

	void default_section(const Section & sec) {
		for (auto & sec2 : sections)
			for (const auto & val : sec)
				sec2.second.insert(val);
	}

	void strip_trailing_comments() {
		const std::locale loc{ "C" };
		for (auto & sec : sections)
			for (auto & val : sec.second) {
				detail::rtrim2(val.second, [this](CharT ch) { return format->is_comment(ch); });
				detail::rtrim(val.second, loc);
			}
	}

	void clear() {
		sections.clear();
		errors.clear();
	}

private:
	using Symbols = std::vector<std::pair<String, String>>;

	const Symbols local_symbols(const String & sec_name, const Section & sec) const {
		Symbols result;
		for (const auto & val : sec)
			result.emplace_back(format->local_symbol(val.first), format->global_symbol(sec_name, val.first));
		return result;
	}

	const Symbols global_symbols() const {
		Symbols result;
		for (const auto & sec : sections)
			for (const auto & val : sec.second)
				result.emplace_back(format->global_symbol(sec.first, val.first), val.second);
		return result;
	}

	bool replace_symbols(const Symbols & syms, Section & sec) const {
		auto changed = false;
		for (auto & sym : syms)
			for (auto & val : sec)
				changed |= detail::replace(val.second, sym.first, sym.second);
		return changed;
	}
};

} // namespace inipp

2.example.ini

3.example.cpp

#include <fstream>
#include "inipp.h"

int main() {
	inipp::Ini<char> ini;
	std::ifstream is("example.ini");
	ini.parse(is);
	std::cout << "raw ini file:" << std::endl;
	ini.generate(std::cout);
	ini.default_section(ini.sections["DEFAULT"]);
	ini.interpolate();
	std::cout << "ini file after default section and interpolation:" << std::endl;
	ini.generate(std::cout);
	int compression_level = -1;
	inipp::get_value(ini.sections["bitbucket.org"], "CompressionLevel", compression_level);
	std::cout << "bitbucket.org compression level: " << compression_level << std::endl;
	return 0;
}

到此这篇关于C++实现ini文件读写的示例代码的文章就介绍到这了,更多相关C++读写ini文件内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯