Cách dễ nhất để phân tích cú pháp tệp INI trong C ++ là gì?


89

Tôi đang cố gắng phân tích cú pháp tệp INI bằng C ++. Bất kỳ lời khuyên về cách tốt nhất để đạt được điều này là gì? Tôi có nên sử dụng các công cụ Windows API để xử lý tệp INI (mà tôi hoàn toàn không quen thuộc), một giải pháp nguồn mở hay cố gắng phân tích cú pháp thủ công?

Câu trả lời:


112

Bạn có thể sử dụng các hàm API của Windows, chẳng hạn như GetPrivateProfileString ()GetPrivateProfileInt () .


4
GetPrivateProfileInt () và các hàm khác không được MSDN khuyến nghị, vì chúng đã lỗi thời và vẫn chỉ được cung cấp để tương thích với các hệ thống 16-bit cũ hơn. Thay vì sử dụng cách tiếp cận khác. msdn.microsoft.com/en-us/library/windows/desktop/…
Zdeno Pavlik

Chúng đã lỗi thời vì MS không muốn bạn sử dụng các tệp ini nữa, chúng vẫn rất lý tưởng nếu bạn thực sự muốn đọc hoặc ghi các tệp như vậy.
Neil

114

Nếu bạn cần một giải pháp đa nền tảng, hãy thử thư viện Tùy chọn chương trình của Boost .


1
tôi xin đề nghị thư viện này quá
varnie

21
Đây là cách để đi, tôi không hiểu tại sao mọi người chỉ up-vote câu trả lời không chung chung.
Ramadheer Singh

17
@Gollum, có vẻ như Windows là một phụ thuộc nhất định. Sử dụng thư viện Tùy chọn Chương trình có nghĩa là sử dụng một phụ thuộc khác. Đôi khi đó không phải là vấn đề lớn, đôi khi nó là như vậy.
IJ Kennedy

5
@malat Tôi đang bối rối, tôi đã không đề cập đến việc giảm giá?
sjdowling

2
Anh ấy đang cố đọc một tệp INI hiện có, Sử dụng boost không phải là một câu trả lời vì nó đang sử dụng định dạng giống như INI.
Lothar

22

Tôi chưa bao giờ phân tích cú pháp tệp ini, vì vậy tôi không thể nói quá cụ thể về vấn đề này.
Nhưng tôi có một lời khuyên:
Đừng phát minh lại bánh xe miễn là bánh xe hiện có đáp ứng yêu cầu của bạn

http://en.wikipedia.org/wiki/INI_file#Accessing_INI_files
http://sdl-cfg.sourceforge.net/
http://sourceforge.net/projects/libini/
http://www.codeproject.com/KB /files/config-file-parser.aspx

Chúc may mắn :)


16

Nếu bạn đang sử dụng Qt

QSettings my_settings("filename.ini", QSettings::IniFormat);

Sau đó, đọc một giá trị

my_settings.value("GroupName/ValueName", <<DEFAULT_VAL>>).toInt()

Có rất nhiều công cụ chuyển đổi khác có thể chuyển đổi các giá trị INI của bạn thành cả loại tiêu chuẩn và loại Qt. Xem tài liệu Qt trên QSettings để biết thêm thông tin.


Không tệ, mặc dù nếu bạn thực hiện các thay đổi, họ sẽ lưu chúng trở lại tệp .ini mà không thực sự cho bạn biết (tức là lệnh gọi hàm hủy sync(), có thể là một điều bất ngờ) và điều đó sẽ phá hủy các nhận xét và thứ tự các biến đã được xác định trước đó ...
Alexis Wilke,


8

câu hỏi này hơi cũ, nhưng tôi sẽ đăng câu trả lời của tôi. Tôi đã thử nghiệm các lớp INI khác nhau (bạn có thể xem chúng trên trang web của tôi ) và tôi cũng sử dụng simpleIni vì tôi muốn làm việc với các tệp INI trên cả windows và winCE. GetPrivateProfileString () của Window chỉ hoạt động với sổ đăng ký trên winCE.

Nó rất dễ đọc với simpleIni. Đây là một ví dụ:

#include "SimpleIni\SimpleIni.h"    
CSimpleIniA ini;
ini.SetUnicode();
ini.LoadFile(FileName);
const char * pVal = ini.GetValue(section, entry, DefaultStr);

6

inih là một trình phân tích cú pháp ini đơn giản được viết bằng C, nó cũng đi kèm với một trình bao bọc C ++. Ví dụ sử dụng:

#include "INIReader.h"    

INIReader reader("test.ini");

std::cout << "version="
          << reader.GetInteger("protocol", "version", -1) << ", name="
          << reader.Get("user", "name", "UNKNOWN") << ", active="
          << reader.GetBoolean("user", "active", true) << "\n";

Tác giả cũng có một danh sách các thư viện hiện có ở đây .



3

Nếu bạn quan tâm đến tính di động của nền tảng, bạn cũng có thể thử Boost.PropertyTree. Nó hỗ trợ ini dưới dạng định dạng liên tục, mặc dù cây thuộc tính của tôi chỉ sâu 1 cấp.


2

Trừ khi bạn có kế hoạch tạo ứng dụng đa nền tảng, sử dụng các lệnh gọi API Windows sẽ là cách tốt nhất để thực hiện. Chỉ cần bỏ qua lưu ý trong tài liệu API về việc chỉ được cung cấp để tương thích ứng dụng 16 bit.



0

Tôi biết câu hỏi này rất cũ, nhưng tôi nghĩ đến nó vì tôi cần một cái gì đó đa nền tảng cho linux, win32 ... Tôi đã viết hàm dưới đây, nó là một hàm duy nhất có thể phân tích cú pháp tệp INI, hy vọng những người khác sẽ thấy nó hữu ích.

quy tắc & lưu ý: buf để phân tích cú pháp phải là một chuỗi kết thúc NULL. Tải tệp ini của bạn vào một chuỗi mảng char và gọi hàm này để phân tích cú pháp. tên phần phải có dấu ngoặc [] xung quanh chúng, chẳng hạn như [MySection] này, các giá trị và phần phải bắt đầu trên một dòng không có dấu cách ở đầu. Nó sẽ phân tích cú pháp tệp với Windows \ r \ n hoặc với phần cuối dòng Linux \ n. Nhận xét nên sử dụng # hoặc // và bắt đầu ở đầu tệp, không nhận xét nào được trộn lẫn với dữ liệu nhập INI. Dấu ngoặc kép và dấu tích được cắt từ cả hai đầu của chuỗi trả về. Khoảng trắng chỉ được cắt bớt nếu chúng nằm ngoài phần trích dẫn. Các chuỗi không bắt buộc phải có dấu ngoặc kép và khoảng trắng được cắt bớt nếu thiếu dấu ngoặc kép. Bạn cũng có thể trích xuất các số hoặc dữ liệu khác, chẳng hạn như nếu bạn có một float, chỉ cần thực hiện một atof (ret) trên bộ đệm ret.

//  -----note: no escape is nessesary for inner quotes or ticks-----
//  -----------------------------example----------------------------
//  [Entry2]
//  Alignment   = 1
//  LightLvl=128
//  Library     = 5555
//  StrValA =  Inner "quoted" or 'quoted' strings are ok to use
//  StrValB =  "This a "quoted" or 'quoted' String Value"
//  StrValC =  'This a "tick" or 'tick' String Value'
//  StrValD =  "Missing quote at end will still work
//  StrValE =  This is another "quote" example
//  StrValF =  "  Spaces inside the quote are preserved "
//  StrValG =  This works too and spaces are trimmed away
//  StrValH =
//  ----------------------------------------------------------------
//12oClocker super lean and mean INI file parser (with section support)
//set section to 0 to disable section support
//returns TRUE if we were able to extract a string into ret value
//NextSection is a char* pointer, will be set to zero if no next section is found
//will be set to pointer of next section if it was found.
//use it like this... char* NextSection = 0;  GrabIniValue(X,X,X,X,X,&NextSection);
//buf is data to parse, ret is the user supplied return buffer
BOOL GrabIniValue(char* buf, const char* section, const char* valname, char* ret, int retbuflen, char** NextSection)
{
    if(!buf){*ret=0; return FALSE;}

    char* s = buf; //search starts at "s" pointer
    char* e = 0;   //end of section pointer

    //find section
    if(section)
    {
        int L = strlen(section);
        SearchAgain1:
        s = strstr(s,section); if(!s){*ret=0; return FALSE;}    //find section
        if(s > buf && (*(s-1))!='\n'){s+=L; goto SearchAgain1;} //section must be at begining of a line!
        s+=L;                                                   //found section, skip past section name
        while(*s!='\n'){s++;} s++;                              //spin until next line, s is now begining of section data
        e = strstr(s,"\n[");                                    //find begining of next section or end of file
        if(e){*e=0;}                                            //if we found begining of next section, null the \n so we don't search past section
        if(NextSection)                                         //user passed in a NextSection pointer
        { if(e){*NextSection=(e+1);}else{*NextSection=0;} }     //set pointer to next section
    }

    //restore char at end of section, ret=empty_string, return FALSE
    #define RESTORE_E     if(e){*e='\n';}
    #define SAFE_RETURN   RESTORE_E;  (*ret)=0;  return FALSE

    //find valname
    int L = strlen(valname);
    SearchAgain2:
    s = strstr(s,valname); if(!s){SAFE_RETURN;}             //find valname
    if(s > buf && (*(s-1))!='\n'){s+=L; goto SearchAgain2;} //valname must be at begining of a line!
    s+=L;                                                   //found valname match, skip past it
    while(*s==' ' || *s == '\t'){s++;}                      //skip spaces and tabs
    if(!(*s)){SAFE_RETURN;}                                 //if NULL encounted do safe return
    if(*s != '='){goto SearchAgain2;}                       //no equal sign found after valname, search again
    s++;                                                    //skip past the equal sign
    while(*s==' '  || *s=='\t'){s++;}                       //skip spaces and tabs
    while(*s=='\"' || *s=='\''){s++;}                       //skip past quotes and ticks
    if(!(*s)){SAFE_RETURN;}                                 //if NULL encounted do safe return
    char* E = s;                                            //s is now the begining of the valname data
    while(*E!='\r' && *E!='\n' && *E!=0){E++;} E--;         //find end of line or end of string, then backup 1 char
    while(E > s && (*E==' ' || *E=='\t')){E--;}             //move backwards past spaces and tabs
    while(E > s && (*E=='\"' || *E=='\'')){E--;}            //move backwards past quotes and ticks
    L = E-s+1;                                              //length of string to extract NOT including NULL
    if(L<1 || L+1 > retbuflen){SAFE_RETURN;}                //empty string or buffer size too small
    strncpy(ret,s,L);                                       //copy the string
    ret[L]=0;                                               //null last char on return buffer
    RESTORE_E;
    return TRUE;

    #undef RESTORE_E
    #undef SAFE_RETURN
}

Cách sử dụng… ví dụ….

char sFileData[] = "[MySection]\r\n"
"MyValue1 = 123\r\n"
"MyValue2 = 456\r\n"
"MyValue3 = 789\r\n"
"\r\n"
"[MySection]\r\n"
"MyValue1 = Hello1\r\n"
"MyValue2 = Hello2\r\n"
"MyValue3 = Hello3\r\n"
"\r\n";
char str[256];
char* sSec = sFileData;
char secName[] = "[MySection]"; //we support sections with same name
while(sSec)//while we have a valid sNextSec
{
    //print values of the sections
    char* next=0;//in case we dont have any sucessful grabs
    if(GrabIniValue(sSec,secName,"MyValue1",str,sizeof(str),&next)) { printf("MyValue1 = [%s]\n",str); }
    if(GrabIniValue(sSec,secName,"MyValue2",str,sizeof(str),0))     { printf("MyValue2 = [%s]\n",str); }
    if(GrabIniValue(sSec,secName,"MyValue3",str,sizeof(str),0))     { printf("MyValue3 = [%s]\n",str); }
    printf("\n");
    sSec = next; //parse next section, next will be null if no more sections to parse
}

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.