How do I tell program to skip comma between quotation in C++? - c++

I have file that contains
Name Age FavSport
Michael, "18,0" , "Soc,cer", Cricket, Hockey
John, "18,0", Cricket
Mitchell, "19,0", Soccer, "Hoc,key"
I am trying to read it into vector of class objects
#include <string>
#include <vector>
#include <fstream>
class Student {
public:
std::string name;
int age;
std::vector<std::string> favSport;
};
class Team {
public:
std::vector<Student> teamVec;
};
When I try to split file by comma it splits commas between quotation like 18,0 it think 18 and 0 separate and gives me error. Also same for Soc,cer, it thinks they are seperate. How can i tell program not to split between quotation. Can any please have a look at code and tell me where i can edit or edit for me please thank you
(I can't use stringstream since i havn't been to that chapter yet, what I have coded is kind of knowledge I know)
int main() {
std::string line;
std::vector<std::string> teamVec;
std::ifstream myfile("team.txt");
if (!myfile)
{
std::cout << "Error" << std::endl;
return -1;
}
bool firstLine = true;
Team myTeam;
while (std::getline(myfile, line))
{
if(firstLine) {
firstLine = false;
} else {
Student temp;
int times = 0;
size_t pos = 0;
std::string token;
while ((pos = line.find(',')) != std::string::npos) {
token = line.substr(0, pos);
times++;
if(times == 1) {
temp.name = token.substr(1,token.size()-2);
} else if(times == 2) {
temp.age = stoi(token);
} else {
temp.favSport.push_back(token);
}
std::cout << token << std::endl;
line.erase(0, pos + 2);
}
temp.favSport.push_back(line);
myTeam.teamVec.push_back(temp);
}
}
// USE myTeam
return 0;
}

Related

How to split string read from text file into array using c++

I want to split the strings on each line of my text file into an array, similar to the split() function in python. my desired syntax is a loop that enters every split-string into the next index of an array,
so for example if my string:
"ab,cd,ef,gh,ij"
, every time I encounter a comma then I would:
datafile >> arr1[i]
and my array would end up:
arr1 = [ab,cd,ef,gh,ij]
a mock code without reading a text file is provided below
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <string.h>
#include <string>
using namespace std;
int main(){
char str[] = "ab,cd,ef,gh,ij"; //" ex str in place of file contents/fstream sFile;"
const int NUM = 5;
string sArr[NUM];//empty array
char *token = strtok(str, ",");
for (int i=0; i < NUM; i++)
while((token!=NULL)){
("%s\n", token) >> sArr[i];
token = strtok(NULL, ",");
}
cout >> sArr;
return 0;
}
In C++ you can read a file line by line and directly get a std::string.
You will found below an example I made with a split() proposal as you requested, and a main() example of reading a file:
Example
data file:
ab,cd,ef,gh
ij,kl,mn
c++ code:
#include <fstream>
#include <iostream>
#include <vector>
std::vector<std::string> split(const std::string & s, char c);
int main()
{
std::string file_path("data.txt"); // I assumed you have that kind of file
std::ifstream in_s(file_path);
std::vector <std::vector<std::string>> content;
if(in_s)
{
std::string line;
std::vector <std::string> vec;
while(getline(in_s, line))
{
for(const std::string & str : split(line, ','))
vec.push_back(str);
content.push_back(vec);
vec.clear();
}
in_s.close();
}
else
std::cout << "Could not open: " + file_path << std::endl;
for(const std::vector<std::string> & str_vec : content)
{
for(unsigned int i = 0; i < str_vec.size(); ++i)
std::cout << str_vec[i] << ((i == str_vec.size()-1) ? ("") : (" : "));
std::cout << std::endl;
}
return 0;
}
std::vector<std::string> split(const std::string & s, char c)
{
std::vector<std::string> splitted;
std::string word;
for(char ch : s)
{
if((ch == c) && (!word.empty()))
{
splitted.push_back(word);
word.clear();
}
else
word += ch;
}
if(!word.empty())
splitted.push_back(word);
return splitted;
}
output:
ab : cd : ef : gh
ij : kl : mn
I hope it will help.
So, a few things to fix. Firstly, arrays and NUM are kind of limiting - you have to fix up NUM whenever you change the input string, so C++ provides std::vector which can resize itself to however many strings it finds. Secondly, you want to call strtok until it returns nullptr once, and you can do that with one loop. With both your for and NUM you call strtok too many times - even after it has returned nullptr. Next, to put the token into a std::string, you would assign using my_string = token; rather than ("%s\n", token) >> my_string - which is a broken mix of printf() formatting and C++ streaming notation. Lastly, to print the elements you've extracted, you can use another loop. All these changes are illustrated below.
char str[] = "ab,cd,ef,gh,ij";
std::vector<std::string> strings;
char* token = strtok(str, ",");
while ((token != nullptr))
{
strings.push_back(token);
token = strtok(NULL, ",");
}
for (const auto& s : strings)
cout >> s >> '\n';
Your code is overly complicated and wrong.
You probably want this:
#include <iostream>
#include <string>
#include <string.h>
using namespace std;
int main() {
char str[] = "ab,cd,ef,gh,ij"; //" ex str in place of file contents/fstream sFile;"
const int NUM = 5;
string sArr[NUM];//empty array
char *token = strtok(str, ",");
int max = 0;
while ((token != NULL)) {
sArr[max++] = token;
token = strtok(NULL, ",");
}
for (int i = 0; i < max; i++)
cout << sArr[i] << "\n";
return 0;
}
This code is still poor and no bound checking is done.
But anyway, you should rather do it the C++ way as suggested in the other answers.
Use boost::split
#include <boost/algorithm/string.hpp>
[...]
std::vector<std::string> strings;
std::string val("ab,cd,ef,gh,ij");
boost::split(strings, val, boost::is_any_of(","));
You could do something like this
std::string str = "ab,cd,ef,gh,ij";
std::vector<std::string> TokenList;
std::string::size_type lastPos = 0;
std::string::size_type pos = str.find_first_of(',', lastPos);
while(pos != std::string::npos)
{
std::string temp(str, lastPos, pos - lastPos);
TokenList.push_back(temp);
lastPos = pos + 1;
pos = str.find_first_of(',', lastPos);
}
if(lastPos != str.size())
{
std::string temp(str, lastPos, str.size());
TokenList.push_back(temp);
}
for(int i = 0; i < TokenList.size(); i++)
std::cout << TokenList.at(i) << std::endl;

Is there any inbuilt function available two get string between two delimiter string in C/C++?

Is there any inbuilt function available to get strings between two delimiter string in C++?
Input string
(23567)=(58765)+(67888)+(65678)
Expected Output
23567
58765
67888
65678
include <iostream>
#include <stdexcept>
#include <string>
#include <sstream>
#include <vector>
std::vector<std::string> tokenize(const std::string& input)
{
std::vector<std::string> result;
std::istringstream stream(input);
std::string thingie; // please choose a better name, my inspiration is absent today
while(std::getline(stream, thingie, '('))
{
if(std::getline(stream, thingie, ')'))
result.push_back(thingie);
else
throw std::runtime_error("expected \')\' to match \'(\'.");
}
return result;
}
void rtc()
{
ifstream myfile(test.txt);
if(myfile.is_open())
while (!myfile.eof())
{
getline(myfile,line);
auto tokens = tokenize(line);
for(auto&& item : tokens)
std::cout << item << '\n';
}
Error C4430 missing type specifier int assumed note:c++ does not support default int
ErrorC2440initializing cannot convertfrom std::vector<_ty>to int
Error C2059syntac error empty declaration
Error C2143syntax error missing;before&&
Error C2059syntax error:')'
Use std::getline:
#include <iostream>
#include <stdexcept>
#include <string>
#include <sstream>
#include <vector>
std::vector<std::string> tokenize(const std::string& input)
{
std::vector<std::string> result;
std::istringstream stream(input);
std::string thingie; // please choose a better name, my inspiration is absent today
while(std::getline(stream, thingie, '('))
{
if(std::getline(stream, thingie, ')'))
result.push_back(thingie);
else
throw std::runtime_error("expected \')\' to match \'(\'.");
}
return result;
}
int main()
{
std::string test = "(23567)=(58765)+(67888)+(65678)";
auto tokens = tokenize(test);
for(auto&& item : tokens)
std::cout << item << '\n';
}
Live example here.
For those not entirely convinced by the awesome robustness of this solution, I specialized this for double inputs between the parentheses, and used boost::lexical_cast to verify the input:
#include <iostream>
#include <stdexcept>
#include <string>
#include <sstream>
#include <vector>
#include <boost/lexical_cast.hpp>
std::vector<double> tokenize(const std::string& input)
{
std::vector<double> result;
std::istringstream stream(input);
std::string thingie; // please choose a better name, my inspiration is absent today
while(std::getline(stream, thingie, '('))
{
if(std::getline(stream, thingie, ')'))
{
try
{
result.push_back(boost::lexical_cast<double>(thingie));
}
catch(...)
{
throw std::runtime_error("This wasn't just a number, was it?");
}
}
else
throw std::runtime_error("expected \')\' to match \'(\'.");
}
return result;
}
int main()
{
std::string test = "(23567)=(58765)+(67888)+(65678)";
auto tokens = tokenize(test);
for(auto&& item : tokens)
std::cout << item << '\n';
test = "(2h567)=(58765)+(67888)+(65678)";
tokens = tokenize(test);
}
Live example here. Now go cry about how bad strtok really is, or how bad/unportable the general <regex> implementations are currently. Also, for those who doubt boost::lexical_cast performance-wise, please see the results for yourself.
strpbrk can be used to find the start of each token
or strcspn can be used to count the characters until the next token
then strspn can be used to find the length of each token.
const char tokenChars[] = "0123456789";
char token = input; // say input is "(23567)=(58765)+(67888)+(65678)"
while( 0 != (token = strpbrk( token, tokenChars )) ) // find token
{
size_t tokenLen = strspn( token, token_chars ); // find length of token
// print out tokenLen characters of token here!
token+= tokenLen; // go to end of token
}
http://www.cplusplus.com/reference/cstring/strspn/
http://www.cplusplus.com/reference/cstring/strcspn/
http://www.cplusplus.com/reference/cstring/strpbrk/
Here's the answer if you wanna use pointers:
char test[32] = "(23567)=(58765)+(67888)+(65678)";
char *output = NULL;
char *pos = (char *)test;
int length = 0;
while (*pos != '\0') {
if(*pos == '(' || *pos == ')' || *pos == '+' || *pos == '=') {
*pos = '\0';
if (length > 0) {
output = new char[length + 1];
strncpy_s(output, length + 1, pos - length, length + 1);
length = 0;
cout << output << endl;
delete [] output;
output = NULL;
}
} else {
length++;
}
pos++;
}
While some of the commentators may hate it I like this:
for (p = std::strtok(input, "+"); p != NULL; p = std::strtok(NULL, "+"))
{
// do more stuff
}
This won't work off the bat - the delimiters need expanding - it demonstrates the ease of use.
const char input[] = "(2av67q)=(ble ble)+(67888)+(qpa)";
int s = 0;
for(int i = 0; input[i]; i++)
{
if ( input[i] == ')' )
{
cout << endl;
s = 0;
}
else if ( input[i] == '(' )
{
s = 1;
continue;
}
else
{
if ( s == 1 )
{
cout << input[i];
}
}
}
result:
2av67q
ble ble
67888
qpa
Here is a solution using a regular expression:
std::vector<std::string> get_numbers(std::string const& s)
{
static std::regex regex(R"(^\((\d+)\)=\((\d+)\)(?:\+\((\d+)\))+$)",
std::regex_constants::ECMAScript
| std::regex_constants::optimize);
std::vector<std::string> results;
std::sregex_iterator matches(s.cbegin(), s.cend(), regex);
for (auto first = matches->cbegin(), last = matches->cend();
last != first;
++first)
{
results.push_back(first->str());
}
return results;
}

Reading Lines after a line in C++ not working

I've spent like 2 hours trying to parse the following bytes from a file :
>Rosalind_6404
CCTGCGGAAGATCGGCACTAGAATAGCCAGAACCGTTTCTCTGAGGCTTCCGGCCTTCCC
TCCCACTAATAATTCTGAGG
>Rosalind_5959
CCATCGGTAGCGCATCCTTAGTCCAATTAAGTCCCTATCCAGGCGCTCCGCCGAAGGTCT
ATATCCATTTGTCAGCAGACACGC
>Rosalind_0808
CCACCCTCGTGGTATGGCTAGGCATTCAGGAACCGGAGAACGCTTCAGACCAGCCCGGAC
TGGGAACCTGCGGGCAGTAGGTGGAAT
I would like to store the word Rosalind_, and store every line, concatenate all, and have just one string having all the lines.
I tried the following code, but it still doesn't work probably, I always miss the last line.
int main()
{
std::ifstream infile("data_set.txt");
map < int, string > ID;
map < int, string > dataSetMap;
int idNumber= 0;
int idDataSetNumber = 0;
std::string line;
std::vector<string> dataSetString;
std::string seqid;
while (!infile.eof() )
{
while(std::getline(infile, line))
{
if ( line.substr(0,1)== ">")
{
conct = "";
seqid = line.substr(1,line.length() - 1);
ID.insert(make_pair( idNumber++, seqid));
lineNumber = 0;
line.clear();
std::string data= "";
if(dataSetString.size()>0)
{
for (int i = 0; i<dataSetString.size(); i++)
{
data+=dataSetString[i];
}
dataSetMap.insert(make_pair(idDataSetNumber++, data));
}
dataSetString.clear();
}
if(!line.empty() )
{
dataSetString.push_back(line);
}
}
}
I'm trying to practice problems solving approaches, and that really gave me headache.
I'm looking for a better approach also.
This code does what you want:
#include <map>
#include <vector>
#include <string>
#include <iostream>
#include <fstream>
int main()
{
std::istream& infile = std::cin;
std::map < int, std::string > ID;
std::map < int, std::string > dataSetMap;
int idNumber= 0;
int idDataSetNumber = 0;
std::string line;
std::vector<std::string> dataSetString;
std::string seqid;
bool success = std::getline(infile, line);
while(success) {
if( line.substr(0,1) == ">" ) {
seqid = line.substr(1,line.length() - 1);
ID.insert(make_pair( idNumber++, seqid));
std::string data;
while(success = std::getline(infile, line)) {
if(line.substr(0,1) == ">") break;
data += line;
}
dataSetMap.insert(make_pair(idDataSetNumber++, data));
} else {
std::cout << "Invalid input file. It needs to start with >SOME_ID" << std::endl;
return 1;
}
}
std::cout << "Parsed data ----------------" << std::endl;
for(std::map<int,std::string>::const_iterator it = dataSetMap.begin(); it != dataSetMap.end(); ++it) {
std::cout << "Id: " << ID[it->first] << std::endl;
std::cout << (it->second) << std::endl;
}
}
It first reads a line from the input file and tries to parse it as an ID. If that fails, it returns an error. Then it reads the data until it finds another ID or EOF. It inserts the data and continues to parse the ID it found if it didn't encounter EOF.
Working demo: http://ideone.com/F4mcrc
Note: This fails when the file is empty, you might want to check for the empty string or a string containing only whitespaces in the else of the ID check and skip it.
EDITED I have corrected my answer and tested it. So no more downvote please!
int main()
{
using namespace std;
ifstream infile("data_set.txt");
map < int, string > ID;
map < int, string > dataSetMap;
int idNumber= 0;
int idDataSetNumber = 0;
string line;
vector<string> dataSetString;
string seqid;
while ( true)
{
bool b=infile.eof();
if(!b)
std::getline(infile, line);
if ( line.substr(0,1)== ">" || b)
{
if(!b)
{
seqid = line.substr(1,line.length() - 1);
ID.insert(make_pair( idNumber++, seqid));
}
line.clear();
string data= "";
if(dataSetString.size()>0)
{
for (unsigned int i = 0; i<dataSetString.size(); i++)
{
data+=dataSetString[i];
}
dataSetMap.insert(make_pair(idDataSetNumber++, data));
}
dataSetString.clear();
if(b)
break;
}
if(!line.empty() )
{
dataSetString.push_back(line);
}
}
return 0;
}

Printing input string words in reverse order

Using if and while/do-while, my job is to print following user's inputs (string value) in reverse order.
For example:
input string value : "You are American"
output in reverse order : "American are You"
Is there any way to do this?
I have tried
string a;
cout << "enter a string: ";
getline(cin, a);
a = string ( a.rbegin(), a.rend() );
cout << a << endl;
return 0;
...but this would reverse the order of the words and spelling while spelling is not what I'm going for.
I also should be adding in if and while statements but do not have a clue how.
The algorithm is:
Reverse the whole string
Reverse the individual words
#include<iostream>
#include<algorithm>
using namespace std;
string reverseWords(string a)
{
reverse(a.begin(), a.end());
int s = 0;
int i = 0;
while(i < a.length())
{
if(a[i] == ' ')
{
reverse(a.begin() + s, a.begin() + i);
s = i + 1;
}
i++;
}
if(a[a.length() - 1] != ' ')
{
reverse(a.begin() + s, a.end());
}
return a;
}
Here is a C-based approach that will compile with a C++ compiler, which uses the stack to minimize creation of char * strings. With minimal work, this can be adapted to use C++ classes, as well as trivially replacing the various for loops with a do-while or while block.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINE_LENGTH 1000
#define MAX_WORD_LENGTH 80
void rev(char *str)
{
size_t str_length = strlen(str);
int str_idx;
char word_buffer[MAX_WORD_LENGTH] = {0};
int word_buffer_idx = 0;
for (str_idx = str_length - 1; str_idx >= 0; str_idx--)
word_buffer[word_buffer_idx++] = str[str_idx];
memcpy(str, word_buffer, word_buffer_idx);
str[word_buffer_idx] = '\0';
}
int main(int argc, char **argv)
{
char *line = NULL;
size_t line_length;
int line_idx;
char word_buffer[MAX_WORD_LENGTH] = {0};
int word_buffer_idx;
/* set up line buffer - we cast the result of malloc() because we're using C++ */
line = (char *) malloc (MAX_LINE_LENGTH + 1);
if (!line) {
fprintf(stderr, "ERROR: Could not allocate space for line buffer!\n");
return EXIT_FAILURE;
}
/* read in a line of characters from standard input */
getline(&line, &line_length, stdin);
/* replace newline with NUL character to correctly terminate 'line' */
for (line_idx = 0; line_idx < (int) line_length; line_idx++) {
if (line[line_idx] == '\n') {
line[line_idx] = '\0';
line_length = line_idx;
break;
}
}
/* put the reverse of a word into a buffer, else print the reverse of the word buffer if we encounter a space */
for (line_idx = line_length - 1, word_buffer_idx = 0; line_idx >= -1; line_idx--) {
if (line_idx == -1)
word_buffer[word_buffer_idx] = '\0', rev(word_buffer), fprintf(stdout, "%s\n", word_buffer);
else if (line[line_idx] == ' ')
word_buffer[word_buffer_idx] = '\0', rev(word_buffer), fprintf(stdout, "%s ", word_buffer), word_buffer_idx = 0;
else
word_buffer[word_buffer_idx++] = line[line_idx];
}
/* cleanup memory, to avoid leaks */
free(line);
return EXIT_SUCCESS;
}
To compile with a C++ compiler, and then use:
$ g++ -Wall test.c -o test
$ ./test
foo bar baz
baz bar foo
This example unpacks the input string one word at a time,
and builds an output string by concatenating in reverse order.
`
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
string inp_str("I am British");
string out_str("");
string word_str;
istringstream iss( inp_str );
while (iss >> word_str) {
out_str = word_str + " " + out_str;
} // while (my_iss >> my_word)
cout << out_str << endl;
return 0;
} // main
`
This uses exactly one each of if and while.
#include <string>
#include <iostream>
#include <sstream>
void backwards(std::istream& in, std::ostream& out)
{
std::string word;
if (in >> word) // Read the frontmost word
{
backwards(in, out); // Output the rest of the input backwards...
out << word << " "; // ... and output the frontmost word at the back
}
}
int main()
{
std::string line;
while (getline(std::cin, line))
{
std::istringstream input(line);
backwards(input, std::cout);
std::cout << std::endl;
}
}
You might try this solution in getting a vector of string's using the ' ' (single space) character as a delimiter.
The next step would be to iterate over this vector backwards to generate the reverse string.
Here's what it might look like (split is the string splitting function from that post):
Edit 2: If you don't like vectors for whatever reason, you can use arrays (note that pointers can act as arrays). This example allocates a fixed size array on the heap, you may want to change this to say, double the size when the current word amount has reached a certain value.
Solution using an array instead of a vector:
#include <iostream>
#include <string>
using namespace std;
int getWords(string input, string ** output)
{
*output = new string[256]; // Assumes there will be a max of 256 words (can make this more dynamic if you want)
string currentWord;
int currentWordIndex = 0;
for(int i = 0; i <= input.length(); i++)
{
if(i == input.length() || input[i] == ' ') // We've found a space, so we've reached a new word
{
if(currentWord.length() > 0)
{
(*output)[currentWordIndex] = currentWord;
currentWordIndex++;
}
currentWord.clear();
}
else
{
currentWord.push_back(input[i]); // Add this character to the current word
}
}
return currentWordIndex; // returns the number of words
}
int main ()
{
std::string original, reverse;
std::getline(std::cin, original); // Get the input string
string * arrWords;
int size = getWords(original, &arrWords); // pass in the address of the arrWords array
int index = size - 1;
while(index >= 0)
{
reverse.append(arrWords[index]);
reverse.append(" ");
index--;
}
std::cout << reverse << std::endl;
return 0;
}
Edit: Added includes, main function, while loop format
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
// From the post
std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems)
{
std::stringstream ss(s);
std::string item;
while(std::getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
return split(s, delim, elems);
}
int main ()
{
std::string original, reverse;
std::cout << "Input a string: " << std::endl;
std::getline(std::cin, original); // Get the input string
std::vector<std::string> words = split(original, ' ');
std::vector<std::string>::reverse_iterator rit = words.rbegin();
while(rit != words.rend())
{
reverse.append(*rit);
reverse.append(" "); // add a space
rit++;
}
std::cout << reverse << std::endl;
return 0;
}
This code here uses string libraries to detect the blanks in the input stream and rewrite the output sentence accordingly
The algorithm is
1. Get the input stream using getline function to capture the spacecs. Initialize pos1 to zero.
2. Look for the first space in the input stream
3. If no space is found, the input stream is the output
4. Else, get the position of the first blank after pos1, i.e. pos2.
5. Save the sub-string bewteen pos1 and pos2 at the beginning of the output sentence; newSentence.
6. Pos1 is now at the first char after the blank.
7. Repeat 4, 5 and 6 untill no spaces left.
8. Add the last sub-string to at the beginning of the newSentence. –
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string sentence;
string newSentence;
string::size_type pos1;
string::size_type pos2;
string::size_type len;
cout << "This sentence rewrites a sentence backward word by word\n"
"Hello world => world Hello"<<endl;
getline(cin, sentence);
pos1 = 0;
len = sentence.length();
pos2 = sentence.find(' ',pos1);
while (pos2 != string::npos)
{
newSentence = sentence.substr(pos1, pos2-pos1+1) + newSentence;
pos1 = pos2 + 1;
pos2 = sentence.find(' ',pos1);
}
newSentence = sentence.substr(pos1, len-pos1+1) + " " + newSentence;
cout << endl << newSentence <<endl;
return 0;
}

Using C++ splitting a section string which is in "[General Setting]" format

I am new to C++, i want to read ini file which has section and key - value pair.
Depending on the section, i want to read the value for corresponding key.
Primarily, i want to read the section which is enclosed in square brackets.
Please help.
Thank you.
For real INI file parsing, I highly suggest the iniparser library. It is excellently documented and easy to use in both C and C++ programs.
If you are only interested in parsing strings of the form [Section name], you could do the following: Find first '[' and last ']' for the string and mark the positions. If both characters have been found, take the section name to be substring between the positions you identified.
Assuming you are using an std::string, you can do the following:
std::string myString = " [Section name] ";
std::size_t start = myString.find_first_of( '[' );
std::size_t end = myString.find_last_of( ']' );
std::string sectionName;
if( start != std::string::npos && end != std::string::npos )
{
sectionName = myString.substr(start + 1, end - 1);
}
std::cout << sectionName << std::endl;
This snippet should show you the basic logic for parsing an ini file and skipping # or ; prefixed lines which are treated as comments
this needs sanity checks for group names, names and values, but should suffice as a demonstration:
#include <iostream>
#include <string>
#include <map>
//
// a simple ini file parser (doesn't check for validity of name field length wise or if it has = char
// to demonstrate basic parsing
//
class IniFile
{
public:
enum { MAX_LINE_LEN = 10*1024 };
class MalformedInputException : public std::exception
{
public:
const char* what() const throw() { return "Input is not a valid or well formed ini file"; }
};
typedef std::map<std::string, std::string> Properties;
IniFile(){}
virtual ~IniFile(){}
std::istream& load(std::istream& in)
{
char lineBuffer[MAX_LINE_LEN];
std::string curGroup = "";
while(!in.eof())
{
in.getline(lineBuffer, MAX_LINE_LEN);
//std::cout<<"line buffer : {"<<lineBuffer<<"}"<<std::endl;
std::string line = trim(lineBuffer);
//std::cout<<"trimmed : {"<<line<<"}"<<std::endl;
// we process only non-empty / non-comment lines
if(line.size() > 0 && line[0] != '#' && line[0] != ';')
{
if(line[0] == '[' && line[line.size() - 1] == ']')
{
curGroup = trim(line.substr(1, line.size() - 2));
}
else if(curGroup.size() > 0)
{
size_t index = line.find_first_of('=');
//todo: needs checks for valid name=value format here
Properties& props = m_props[curGroup]; // this will create new Properties if none exists
props[line.substr(0,index)] = line.substr(index+1);
}
else
{
throw MalformedInputException();
}
}
}
return in;
}
std::ostream& save(std::ostream& os) const
{
std::map<std::string, Properties>::const_iterator iter = m_props.begin();
while(iter != m_props.end())
{
os<<"["<<iter->first<<"]"<<std::endl;
Properties::const_iterator propIter = iter->second.begin();
while(propIter != iter->second.end())
{
os<<propIter->first<<"="<<propIter->second<<std::endl;
propIter++;
}
iter++;
}
return os;
}
std::string trim(const std::string& input)
{
static std::string WHITESPACES = "\r\n \t\b\a";
if(input.size() == 0){ return input; }
else
{
size_t start = 0;
for(size_t index = 0; index < input.size(); index++)
{
if(WHITESPACES.find(input[index]) < WHITESPACES.size())
{
start = index;
}
else
{
break;
}
}
size_t endIndex = input.size() - 1;
if(start == endIndex){ return ""; }
for(; endIndex > start; endIndex--)
{
char c = input.at(endIndex);
if(WHITESPACES.find_first_of(c) >= WHITESPACES.size())
{
break;
}
}
size_t length = endIndex - start + 1;
return input.substr(start, length);
}
}
private:
std::map<std::string, Properties> m_props;
};
inline std::ostream& operator << (std::ostream& os, const IniFile& iniFile)
{
return iniFile.save(os);
}
inline std::istream& operator >> (std::istream& in, IniFile& iniFile)
{
return iniFile.load(in);
}
#include <sstream>
int main(int argc, char** argv)
{
std::ostringstream ss;
ss<<"# sample ini file"<<std::endl;
ss<<"[Group1]"<<std::endl;
ss<<"foo=bar \n"<<std::endl;
ss<<"baz=foz"<<std::endl;
ss<<"; this is another comment"<<std::endl;
ss<<"[Group2]"<<std::endl;
ss<<"blanga=kukoo"<<std::endl;
std::string buf = ss.str();
std::istringstream sin(buf);
IniFile iniFile;
iniFile.load(sin);
iniFile.save(std::cout<<"Ini File Loaded : "<<std::endl);
return 0;
}