2008-08-15 41 views
80

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ở hoặc cố phân tích cú pháp thủ công không?Cách dễ nhất để phân tích cú pháp tệp INI trong C++ là gì?

Trả lời

106

Bạn có thể sử dụng các chức năng API của Windows, suc h là GetPrivateProfileString()GetPrivateProfileInt().

+0

GetPrivateProfileInt() và các chức năng khác không được khuyến khích bởi MSDN, bởi vì họ đã lỗi thời và vẫn được cung cấp chỉ cho khả năng tương thích với baskward cũ, Hệ thống 16 bit. Thay vì sử dụng cách tiếp cận khác. https://msdn.microsoft.com/en-us/library/windows/desktop/ms724345(v=vs.85).aspx –

23

Tôi chưa bao giờ phân tích cú pháp các tệp ini, vì vậy tôi không thể 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 càng lâu càng 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 :)

2

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

109

Nếu bạn cần giải pháp đa nền tảng, hãy thử thư viện Program Options của Boost.

+0

tôi cũng đề nghị thư viện này quá – varnie

+18

đây là cách để đi, tôi không ' t hiểu lý do tại sao mọi người chỉ cần bỏ phiếu cho câu trả lời không chung chung. –

+15

@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à đang phụ thuộc vào một sự phụ thuộc khác. Đôi khi đó không phải là một vấn đề lớn, đôi khi nó được. –

16

Tôi sử dụng SimpleIni. Đó là nền tảng chéo.

+1

không hỗ trợ utf-16 :(tại sao ??? – DiGMi

+0

SimpleIni hiện được lưu trữ trong Github –

+1

https://github.com/brofield/simpleini – Katu

4

Bạn đã thử libconfig; cú pháp rất giống JSON. Tôi thích nó hơn các tập tin cấu hình XML.

8

câu hỏi này hơi cũ, nhưng tôi sẽ đăng câu trả lời của mình. Tôi đã thử nghiệm các lớp INI khác nhau (bạn có thể thấy chúng trên website) 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ả hai cửa sổ và winCE. GetPrivateProfileString của cửa sổ() chỉ hoạt động với sổ đăng ký trên winCE.

Rất dễ đọc với simpleIni. Dưới đâ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); 
13

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ó một loạt các chuyển đổi khác mà chuyển đổi giá trị INI của bạn vào cả hai 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.

+0

Không tệ, mặc dù nếu bạn thực hiện thay đổi, chúng sẽ lưu chúng trở lại. file ini mà không thực sự nói với bạn (tức là destructor gọi 'sync()', có thể là một sự ngạc nhiên) và phá hủy các chú thích và trật tự trong đó các biến được định nghĩa trước đó ... –

3

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

5

inih là trình phân tích cú pháp ini đơn giản được viết bằng C, nó đi kèm với trình bao bọc C++. Ví dụ về cách 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ó danh sách các thư viện hiện có here.

0

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

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

// -----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 
} 

Làm thế nào để 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 
} 
Các vấn đề liên quan