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
Bạn có thể sử dụng các chức năng API của Windows, suc h là GetPrivateProfileString() và GetPrivateProfileInt().
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 :)
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.
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.
tôi cũng đề nghị thư viện này quá – varnie
đâ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. –
@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. –
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.
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);
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.
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 đó ... –
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ỉ.
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.
Có lẽ một cuối answer..But, có giá trị biết options..If bạn cần một giải pháp đa nền tảng, chắc chắn bạn có thể thử GLib ,, nó thú vị .. (https://developer.gnome.org/glib/stable/glib-Key-value-file-parser.html)
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
}
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 –