Hi
The problem with Jackson Marie's approach is that it presumes you know the key names. If you do know them, then GetPrivateProfileString (and GetPrivateProfileInt) is a good way to go. But if you need to discover the key names, then you need to use GetPrivateProfileSection instead.
The reason you only saw the first name-value pair is because the text returned by GetPrivateProfileSection contains embedded nulls. So you've got to jump over them. e.g. (in C++ notation)
"key1=12\0key2=14\0key3=13\0\0" |
If you're using a pointer based approach, you start off with your pointer (e.g. pSubstr) at the start of the buffer. Then you advance it by strlen(pSubstr) + 1 (the +1 is what jumps over the null.) The double null (i.e. an empty string) at the end is what allows you to spot the actual end of the data (you'll hit that second null when you jump over the last of those which marks the end of an actual substring.)
The following code shows how to do this. Note that it's up to you to split the name-value pairs on the '=' char. Also note there's minimal error handling in this code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
|
// test.cpp
#define WIN32_LEAN_AND_MEAN
// Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <iostream>
#include <string>
#include <vector>
typedef std::pair<std::string, std::string> NameValuePair;
void
loadIniFile( const std::string& iniFilePath,
const std::string& sectionName,
std::vector<NameValuePair>& nameValuePairs ) {
const int bufferSize = 10000;
char buffer[bufferSize] = "";
int charsRead = 0;
charsRead = GetPrivateProfileSection( sectionName.c_str(),
buffer,
bufferSize,
iniFilePath.c_str() );
// if there isn't enough space, returns bufferSize - 2
// if we got some data...
if( (0 < charsRead) && ((bufferSize - 2) > charsRead) ) {
// walk the buffer extracting values
// start at the beginning (const to remind us not to
// change the contents of the buffer)
const char* pSubstr = buffer;
// while we have non-empty substrings...
while('\0' != *pSubstr) {
// length of key-value pair substring
size_t substrLen = strlen(pSubstr);
// split substring on '=' char
const char* pos = strchr(pSubstr, '=');
if(NULL != pos) {
// todo: remove "magic number" for buffer size
char name [256] = "";
char value[256] = "";
// if you're not using VC++ you'll prob. need to replace
// _countof(name) with sizeof(name)/sizeof(char) and
// similarly for value. Plus replace strncpy_s with plain
// old strncpy.
strncpy_s(name , _countof(name), pSubstr, pos - pSubstr);
strncpy_s(value, _countof(value), pos + 1, substrLen - (pos - pSubstr));
nameValuePairs.push_back(NameValuePair(name, value));
}
// jump over the current substring plus its null
pSubstr += (substrLen + 1);
}
}
}
void dumpNameValuePairs(const std::vector<NameValuePair>& nameValuePairs) {
typedef std::vector<NameValuePair>::size_type size_type;
const size_type count = nameValuePairs.size();
for(size_type index = 0; count > index; ++index) {
const NameValuePair nameValuePair = nameValuePairs[index];
std::cout << nameValuePair.first << " = " << nameValuePair.second << "\n";
}
std::cout << std::endl;
}
int main(int argc, char* argv[]) {
if(3 != argc) {
std::cerr << "ini section dump test\n"
"\n"
"test.exe ini-file-path section-name\n"
"\n"
"Notes\n"
"- full path to ini-file must be specified, or Windows will\n"
" assume the file is in the Windows folder (typically C:\\Windows)\n"
"- paths and section names containing spaces must be quoted" << std::endl;
} else {
std::string iniFilePath = argv[1];
std::string sectionName = argv[2];
std::vector<NameValuePair> nameValuePairs;
loadIniFile(iniFilePath, sectionName, nameValuePairs);
dumpNameValuePairs(nameValuePairs);
}
return 0;
}
|
for the following ini file
1 2 3 4 5 6 7
|
; hello.ini
[message]
message text=hello world
sender=andywestken
recipient=wesly
website=cplusplus
|
and the command line (adjust your file path as required...)
test.exe w:\testing\hello.ini message |
the output is
message text = hello world
sender = andywestken
recipient = wesly
website = cplusplus |
Andy
PS Regarding
I have one doubt is that in ini file every line has any EOL character |
An ini-file is just a text file with either a section name or key name-value pair per line (plus blank and comment lines). So it (the file) has plenty of EOLs (if edited using Notepad.exe, etc. they'll be the normal Windows version. i.e. the pair of chars, \r\n).
But the ini-file API never returns them to you, so they are not relevant to the ini-file reading code, unless you open an ini-file and parse it directly yourself (e.g. using ifstream). When multiple values are returned, they're separated by the null char ('\0') as mentioned above.
PPS If you have a single ini-file section with 1000 name-value pairs, you prob. need to rethink how you're storing your data!
Incidentally, if you need a list of the sections in an ini-file, the code above should more or less work with GetPrivateProfileSectionNames. But while the section names are also separated by nulls, there is no value to split from the name.