This is my c++ code to retrieve html data with cgi.
char* fileContentLength;
int nContentLength;
fileContentLength = getenv("CONTENT_LENGTH");
if(fileContentLength == NULL)
return -1;
nContentLength = atoi(fileContentLength);
if(nContentLength == 0)
return -1;
data = (char*) malloc(nContentLength+1);
if(data == NULL)
return -1;
memset(data, 0, nContentLength+1);
if(fread(data, 1, nContentLength, stdin) == 0)
return -1;
if(ferror(stdin))
After executing this code, i got the below result to the variable "data".
f0=fname0&l0=lname0&f1=fname1&l1=lname1&f2=fname2&l2=lname2&f3=&l3=
Here f0,l0,f1,l1 are name of the input box of the HTML page. From this string i need to separate the values like fname0, lname0,fname1,lname1 and so on. I used sscanf function. but i could not retrieve the correct result. How can i assign the values from the above string to a local variable called firstname and lastname.
Check out e.g. the strtok function. Use it in a loop to split at the '&' to get all the key-value pairs into a vector (for example). Then go though the vector splitting each string (you can use strtok again here) at the '=' character. You can put the keys and values in a std::map, or use directly.
For an even more C++-specific method, use e.g. std::string::find and std::string::substr instead of strtok. Then you can put keys and values directly into the map instead of temporary storing them as strings in a vector.
Edit: How to get the last pair
The last key-value pair is not terminated by the '&' character, so you have to check for the last pair after the loop. This can be done by having a copy of your string, and then get the substring after the last '&'. Something like this perhaps:
char *copy = strdup(data);
// Loop getting key-value pairs using `strtok`
// ...
// Find the last '&' in the string
char *last_amp_pos = strrchr(copy, '&');
if (last_amp_pos != NULL && last_amp_pos < (copy + strlen(copy)))
{
last_amp_pos++; // Increase to point to first character after the ampersand
// `last_amp_pos` now points to the last key-value pair
}
// Must be free'd since we allocated a copy above
free(copy);
The reason we need to use a copy of the string, if because strtok modifies the string.
I still would recommend to you use C++ strings instead of relying on the old C functions. It would probably simplify everything, including you not needing to add the extra check for the last key-value pair.
Related
I have a small assignment which partly requires me to take inputs from a file in the form of strings and place them into char arrays so I can check if the string contains any '*' character at the end of it.
I have been able to extract the strings from the files successfully, however i have failed to find a way in which to place them in char arrays so i can process them.
I would be very grateful if someone would let me know how to place a string into char arrays using cstring library. Please keep in mind that the strings are taken from a file and not as user input.
some of the ways i tried is the following:
//Try 1
char CstringArray[] = LineFromFile;
//Try 2
char CstringArray[100] = LineFromFile;
//Try 3
ifstream Test("Test.txt");
Test>>CstringArray;
//Try 4
ifstream Test("Test.txt");
Test>>CstringArray[0];
Thank you very much
Since this is an assignment, your professor will probably not be happy with you using all of C++'s functionality, particularly if you don't understand it, but since it's a one liner I figured I'd tell you how I'd print all strings ending in an asterisks. Given that you have successfully opened the file to ifstream Test you can do:
copy_if(istream_iterator<string>(Test), istream_iterator<string>(), ostream_iterator<string>(cout, " "), [](const auto& i) { return !empty(i) && i.back() == '*'; })
EDIT:
I'm using an istream_iterator to read in each string in Test and istream_iterator, I'm operating on these values immediately, but if you needed to start by saving all the strings to a vector<string> you could also do this: vector<string> CstringArray{ istream_iterator<string>(Test), istream_iterator<string>() }
I'm using an ostream_iterator to directly stream out my selected strings rather than storing them
I'm using copy_if to iterate over all the strings that are streamed in, selecting only those that meet a given criteria
I'm using the lambda: [](const auto& i) { return !empty(i) && i.back() == '*'; } to conditionally select non-empty strings which end with an asterisks character
I have a file which contains records of students in the following format.
Umar|Ejaz|12345|umar#umar.com
Majid|Hussain|12345|majid#majid.com
Ali|Akbar|12345|ali#geeks-inn.com
Mahtab|Maqsood|12345|mahtab#myself.com
Juanid|Asghar|12345|junaid#junaid.com
The data has been stored according to the following format:
firstName|lastName|contactNumber|email
The total number of lines(records) can not exceed the limit 100. In my program, I've defined the following string variables.
#define MAX_SIZE 100
// other code
string firstName[MAX_SIZE];
string lastName[MAX_SIZE];
string contactNumber[MAX_SIZE];
string email[MAX_SIZE];
Now, I want to pull data from the file, and using the delimiter '|', I want to put data in the corresponding strings. I'm using the following strategy to put back data into string variables.
ifstream readFromFile;
readFromFile.open("output.txt");
// other code
int x = 0;
string temp;
while(getline(readFromFile, temp)) {
int charPosition = 0;
while(temp[charPosition] != '|') {
firstName[x] += temp[charPosition];
charPosition++;
}
while(temp[charPosition] != '|') {
lastName[x] += temp[charPosition];
charPosition++;
}
while(temp[charPosition] != '|') {
contactNumber[x] += temp[charPosition];
charPosition++;
}
while(temp[charPosition] != endl) {
email[x] += temp[charPosition];
charPosition++;
}
x++;
}
Is it necessary to attach null character '\0' at the end of each string? And if I do not attach, will it create problems when I will be actually implementing those string variables in my program. I'm a new to C++, and I've come up with this solution. If anybody has better technique, he is surely welcome.
Edit: Also I can't compare a char(acter) with endl, how can I?
Edit: The code that I've written isn't working. It gives me following error.
Segmentation fault (core dumped)
Note: I can only use .txt file. A .csv file can't be used.
There are many techniques to do this. I suggest searching StackOveflow for "[C++] read file" to see some more methods.
Find and Substring
You could use the std::string::find method to find the delimiter and then use std::string::substr to return a substring between the position and the delimiter.
std::string::size_type position = 0;
positition = temp.find('|');
if (position != std::string::npos)
{
firstName[x] = temp.substr(0, position);
}
If you don't terminate a a C-style string with a null character there is no way to determine where the string ends. Thus, you'll need to terminate the strings.
I would personally read the data into std::string objects:
std::string first, last, etc;
while (std::getline(readFromFile, first, '|')
&& std::getline(readFromFile, last, '|')
&& std::getline(readFromFile, etc)) {
// do something with the input
}
std::endl is a manipulator implemented as a function template. You can't compare a char with that. There is also hardly ever a reason to use std::endl because it flushes the stream after adding a newline which makes writing really slow. You probably meant to compare to a newline character, i.e., to '\n'. However, since you read the string with std::getline() the line break character will already be removed! You need to make sure you don't access more than temp.size() characters otherwise.
Your record also contains arrays of strings rather than arrays of characters and you assign individual chars to them. You either wanted to yse char something[SIZE] or you'd store strings!
I'm trying to parse web data coming from a server, and I'm trying to find a more stl version of what I had.
My old code consisted of a for() loop and checked each character of the string against a set of escape characters and used a stringstream to collect the rest. As I'm sure you can imagine, this sort of loop leads to being a high point of failure when reading web data, as I need strict syntax checking.
I'm trying to instead start using the string::find and string::substr functions, but I'm unsure of the best implementation to do it with.
Basically, I want to read a string of data from a server, different data, separated by a comma. (i.e., first,lastname,email#email.com) and separate it at the commas, but read the data in between.
Can anyone offer any advice?
I'm not sure what kind of data are you parsing, but it's always a good idea to use a multi layer architecture. Each layer should implement an abstract function, and each layer should only do one job (like escaping characters).
The number of layers you use depends on the actual steps needed to decode the stream
for your problem I suggest the following layers:
1st: tokenize by ',' and '\n': convert in to some kind of vector of strings
2nd: resolve escapes: decode escape characers
you should use std::stringstream, and process the characters with a loop. unless your format is REALLY simple (like only a single separator character, without escapes), you can't really use any standard function.
For the learning experience, this is the code I ended up using to parse data into a map. You can use the web_parse_resurn.err to see if an error was hit, or use it for specific error codes.
struct web_parse_return {
map<int,string> parsedata;
int err;
};
web_parse_return* parsewebstring(char* escapechar, char* input, int tokenminimum) {
int err = 0;
map<int,string> datamap;
if(input == "MISSING_INFO") { //a server-side string for data left out in the call
err++;
}
else {
char* nTOKEN;
char* TOKEN = strtok_s(input, escapechar,&nTOKEN);
if(TOKEN != 0) { //if the escape character is found
int tokencount = 0;
while(TOKEN != 0) {//since it finds the next occurrence, keep going
datamap.insert(pair<int,string>(tokencount,TOKEN));
TOKEN = strtok_s(NULL, escapechar,&nTOKEN);
tokencount++;
}
if(tokencount < tokenminimum) //check that the right number was hit
err++; //other wise, up the error count
}
else {
err++;
}
}
web_parse_return* p = new web_parse_return; //initializing a new struct
p->err = err;
p->parsedata = datamap;
return p;
}
I am trying to decode an input file that looks something like this:
abbaabbbbaababbaabababaabababaabbababaabababababababa...
and compare it to a makeshift mapping I have made using two arrays
int secretNumber[10];
string coding[10];
coding[0]="abb";
coding[1]="aabbbba";
coding[2]="abab";
...
I am not sure how I can start off by reading the first character which is 'a' then check if it's in the coding array. If it is print out the secretCoding and move the next character b. Else if it's not in the array then add the next character to the first in a string and check to see if "ab" is in the array and if that isn't either add the next character which makes "abb" and so on.
Something like this:
while (!(readFile.eof()) ){
for(int i=0; i<10; i++){
if(stringOfChars==coding[i]){
cout << secretNumber[i] <<endl;
//Now increment to next char
}
else{
//combine the current string with the next character
}
}
}
Question: How do I go about reading in a character if its a match move to next character if not combine current character and the next character until there's a match.
You sould use a design pattern called interpreter.
Here is a link to a c++ version.
If you want a solution that works for arbitrary input sizes, i.e. which doesn't store the entire input in memory, then you can use a queue (e.g. std::deque<char>) to read in a handful of characters at a time, pushing data in from the back. Then you check if the queue still has three, four or five characters left, and if so compare them to your patterns; if there's a match, you pop the corresponding characters off from the front of the queue.
I'm not sure but perhaps it seems like you are trying to implement the LZW compression algorithm. If that is the case, then you would have to change your approach a little. If you have decided that your secret code are integers, then you would have to assign a code to all the elements of the initial contents of the dictionary. The initial dictionary is basically all the strings in your source alphabet of size 1. In your case it would be "a to z", or only "a" and "b" if you are keeping it simple.
The other thing is that you need to look through your dictionary for any existing string which has been assigned a code. The best way to do that is to use STL map container which could map strings to integers in your case. Also, its a good idea to place a restriction on the size to which the dictionary could grow as new strings continue to be added to it.
Overall,
Use std::map< std::string, int > dictionary; as your dictionary for strings such as a, b, aa, ab, aab, etc... and the matching code for it.
The coding[0], coding[1] would not be required as they strings would serve as the key in this dictionary.
The secretNumber[0], secretNumber[1] also would not be needed as the value would for a key would give the secretNumber.
Here is what it may look like:
std::map< std::string, int > dictionary;
int iWordCount = 0;
/*
Initialize the dictionary with the code for strings of length 1 in your source alphabet.
*/
dictionary["a"] = 0;
dictionary["b"] = 1;
iWordCount = 2; // We would be incrementing this as we keep on adding more strings to the dictionary.
std::string newWord = "", existingWord = "";
while (!(readFile.eof()) ){
/*
I'm assuming the next character is read in the variable "ch".
*/
newWord += ch;
if ( dictionary.count(newWord) != 0 ) { // Existing word.
/*
Do something
*/
}
else { // We encountered this word for the first time.
/*
Do something else
*/
}
}
I am wondering if it is possible to cut down how many size_t variables I use here. Here is what I have:
std::size_t found, found2, found3, found4 /* etc */;
Each has its own string to find:
found1 = msg.find("string1");
found2 = msg.find("string2");
found3 = msg.find("string3");
found4 = msg.find("string4");
// etc
If the word is found, then it will discard and prevent the message to be shown:
if (found1 != std::string::npos)
{
SendMsg("You cannot say that word!");
}
I have else if statements until found21. I'd like to cut everything down in size, so it would be clean, but I don't have a clue how to do it. I would also like it to lowercase the word. I have never used tolower at all either, so I would appreciated it if someone would help me.
To lowercase a string, you can do
std::transform(msg.begin(), msg.end(), msg.begin(), std::tolower);
Transform takes a begin and end iterator as the first and second arguments, and for each element in that range, applies the fourth argument (a function) and assigns it to what the third iterator is pointing to and increments it. By passing msg.begin() as both the first and third arguments, it will assign the result of the function to what it passed to it. So transform will basically do this:
for (auto src = begin(msg), dst = begin(msg); src != end(msg); ++src, ++dst)
*dst = tolower(*src);
but using transform is so much nicer.
To check whether a string contains any of a list of substrings, you can use a for loop with a vector:
vector<string> bad_strings { "bad word 1", "bad word 2", "etc" };
for (auto i = begin(bad_strings); i != end(bad_strings); ++i)
if (msg.find(*i)) {
SendMsg("You cannot say that word!");
break; // stop when you find it matches even one bad string
}