广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++实现ini文件读写的示例代码
  • 802
分享到

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

2024-04-02 19:04:59 802人浏览 安东尼
摘要

目录介绍1.使用INIReader.h头文件1.INIReader.h2.test.ini3.INIReaderTest.cpp2.使用ini.h头文件1.ini.h2.config

介绍

一般的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被设置为空字符串

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

  • 如果行为空或以;开始然后什么都没发生。
  • 否则,如果行以 [ 开头,则section将更改为 [ 和 ] 之间的字符串。如果行不以 ] 结尾,则报告错误。
  • 否则,如果行包含 = 符号,则 = 之前的所有字符都被视为变量, = 之后的所有字符都被视为值。两者都被修剪。如果变量之前已经赋值,则会报告错误。否则,将相应的赋值添加到该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文件内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C++实现ini文件读写的示例代码

本文链接: https://www.lsjlt.com/news/148132.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作