Basically I want to validate a string against a mask which is in the DB however to validate against it I need to assign a rule to that mask i.e [D] = 0<=10. So what I have done is retrieved that mask and extracted the [] from the letters and stored them in two different vectors, so my question is, that can you assign a rule to various cells with the vector
i.e
a[0] = 0<=10
a[1] = "H"
something along the lines of that below is my code bear in mind that the string in the top is not from the DB it is just a string i created assuming it is from the DB because the process will be the same
string s("[sh][a][mar][i]");
vector< vector<char> > Vect;
vector<char> vect;
int i = 0;
while(i < s.size()) {
if(s[i]=='[') {
i++;
vect.push_back(s[i]);
i++;
}
else if(s[i] == ']') {
i++;
Vect.push_back(vect);
vect.clear();
}
else {
vect.push_back(s[i]);
i++;
}
}
vector< vector<char> >::iterator it;
vector<char>::iterator itera;
vector<std::string> vectString;
for (it = Vect.begin() ; it != Vect.end() ; ++it ) {
string a;
for (itera = it->begin() ; itera != it->end() ; ++itera) {
cout << *itera;
a += *itera;
}
vectString.push_back(a);
}
I don't really understand your question, but here is what I can suggest:
Instead of using a huge if-else-if, try use std::string::find and std::string::substr to extract elements.
I don't really see the reason you transform std::string to char and then reverse it. Use std::string::find and std::string::substr to get vectString in one step may be a better idea.
std::string offers powerful functions, if you are not familiar with it, you may want to take a look at : http://www.cplusplus.com/reference/string/string/
Do you want to verify if a given string can be accepted by some regular expressions?
If this is the case, why don't you just use some regular expression objects which representing your rules and then check whether its matching result equals to your original string?
Related
I need to find the most frequently occurring word and return that value. I must use hash maps and the fuction would take a file name. This is what I've done so far but im very confused.
int most_frequent_word(string filename)
{
string words;
ifstream in(filename.c_str());
unordered_map<string, int> word_map;
while(in >> words)
{
for(int i = 0; i < 100; i++)
{
word_map[words[i]]++;
}
}
return words;
}
any help would be appreciated it. Thanks!
There are several issues in your code that might cause it to work not as expected.
First is for i loop. Why would you need taht loop at all? Leave it like this, you need to count words.
while(in >> words)
{
word_map[words]++;
}
Rename words to word, actually you are reading one word here in >> words.
The third is return statement. You cannot return string when it is declared that function returns int.
However there is nothing to return yet, because so far we only know the number each word occurred. Run a loop to find max value.
int result = 0;
for(unordered_map<string, int>::iterator it = word_map.begin(); it != word_map.end(); it++)
result = max(result, it->second);
return result;
Here word_map consists of pairs of a word and its number of occurrences. We need to iterate over all of these pairs looking for max occurrences. To do this we use iterator it.
I'm also confused!
for(int i = 0; i < 100; i++)
{
word_map[words[i]]++;
}
What are you doing here? Where does the 100 come from? Why do you care for single letters of your words (which is what words[i] gets you)?
If I understand your task correctly, wouldn't it suffice to
++word_map[words];
instead?
Also why do you return words? It's a string, and your function should return and int. Instead find the largest value in your map and you're done.
I want to read a string with integers and whitespaces into an array. For example I have a string looks like 1 2 3 4 5, and I want to convert it into an integer array arr[5]={1, 2, 3, 4, 5}. How should I do that?
I tried to delete the whitespaces, but that just assign the whole 12345 into every array element. If I don't everything element will all assigned 1.
for (int i = 0; i < str.length(); i++){
if (str[i] == ' ')
str.erase(i, 1);
}
for (int j = 0; j < size; j++){ // size is given
arr[j] = atoi(str.c_str());
}
A couple of notes:
Use a std::vector. You will most likely never know the size of an input at compile time. If you do, use a std::array.
If you have C++11 available to you, maybe think about stoi or stol, as they will throw upon failed conversion
You could accomplish your task with a std::stringstream which will allow you to treat a std::string as a std::istream like std::cin. I recommend this way
alternatively, you could go the hard route and attempt to tokenize your std::string based on ' ' as a delimiter, which is what it appears you are trying to do.
Finally, why reinvent the wheel if you go the tokenization route? Use Boost's split function.
Stringstream approach
std::vector<int> ReadInputFromStream(const std::string& _input, int _num_vals)
{
std::vector<int> toReturn;
toReturn.reserve(_num_vals);
std::istringstream fin(_input);
for(int i=0, nextInt=0; i < _num_vals && fin >> nextInt; ++i)
{
toReturn.emplace_back(nextInt);
}
// assert (toReturn.size() == _num_vals, "Error, stream did not contain enough input")
return toReturn;
}
Tokenization approach
std::vector<int> ReadInputFromTokenizedString(const std::string& _input, int _num_vals)
{
std::vector<int> toReturn;
toReturn.reserve(_num_vals);
char tok = ' '; // whitespace delimiter
size_t beg = 0;
size_t end = 0;
for(beg = _input.find_first_not_of(tok, end); toReturn.size() < static_cast<size_t>(_num_vals) &&
beg != std::string::npos; beg = _input.find_first_not_of(tok, end))
{
end = beg+1;
while(_input[end] == tok && end < _input.size())
++end;
toReturn.push_back(std::stoi(_input.substr(beg, end-beg)));
}
// assert (toReturn.size() == _num_vals, "Error, string did not contain enough input")
return toReturn;
}
Live Demo
Your code arr[j] = atoi(str.c_str()); is fault. The str is a string, not a char. When you used atoi(const char *), you should give the &char param. So the correct code is arr[j] = atoi(&str[j]). By the way, if you want to change the string to int, you could use the function arr[j] = std::stoul(str). I hope this can help you.
You have modified/parsing the string in one loop, but copying to integer array in another loop. without setting any marks, where all the embedded integers in strings start/end. So we have to do both the actions in single loop.
This code is not perfect, but to give you some idea; followed the same process you followed, but used vectors.
string str = "12 13 14";
vector<int> integers;
int start=0,i = 0;
for (; i < str.length(); i++){
if (str[i] == ' ')
{
integers.push_back(atoi(str.substr(start,i).c_str()));
start = i;
}
}
integers.push_back(atoi(str.substr(start,i).c_str()));
I am using two dynamic arrays to read from a file. They are to keep track of each word and the amount of times it appears. If it has already appeared, I must keep track in one array and not add it into the other array since it already exists. However, I am getting blank spaces in my array when I meet a duplicate. I think its because my pointer continues to advance, but really it shouldn't. I do not know how to combat this. The only way I have was to use a continue; when I print out the results if the array content = ""; if (*(words + i) == "") continue;. This basically ignores those blanks in the array. But I think that is messy. I just want to figure out how to move the pointer back in this method. words and frequency are my dynamic arrays.
I would like guidance in what my problem is, rather than solutions.
I have now changed my outer loop to be a while loop, and only increment when I have found the word. Thank you WhozCraig and poljpocket.
Now this occurs.
Instead of incrementing your loop variable [i] every loop, you need to only increment it when a NEW word is found [i.e. not one already in the words array].
Also, you're wasting time in your inner loop by looping through your entire words array, since words will only exist up to index i.
int idx = 0;
while (file >> hold && idx < count) {
if (!valid_word(hold)) {
continue;
}
// You don't need to check past idx because you
// only have <idx> words so far.
for (int i = 0; i < idx; i++) {
if (toLower(words[i]) == toLower(hold)) {
frequency[i]++;
isFound = true;
break;
}
}
if (!isFound) {
words[idx] = hold;
frequency[idx] = 1;
idx++;
}
isFound = false;
}
First, to address your code, this is what it should probably look like. Note how we only increment i as we add words, and we only ever scan the words we've already added for duplicates. Note also how the first pass will skip the j-loop entirely and simply insert the first word with a frequency of 1.
void addWords(const std::string& fname, int count, string *words, int *frequency)
{
std::ifstream file(fname);
std::string hold;
int i = 0;
while (i < count && (file >> hold))
{
int j = 0;
for (; j<i; ++j)
{
if (toLower(words[j]) == toLower(hold))
{
// found a duplicate at j
++frequency[j];
break;
}
}
if (j == i)
{
// didn't find a duplicate
words[i] = hold;
frequency[i] = 1;
++i;
}
}
}
Second, to really address your code, this is what it should actually look like:
#include <iostream>
#include <fstream>
#include <map>
#include <string>
//
// Your implementation of toLower() goes here.
//
typedef std::map<std::string, unsigned int> WordMap;
WordMap addWords(const std::string& fname)
{
WordMap words;
std::ifstream inf(fname);
std::string word;
while (inf >> word)
++words[toLower(word)];
return words;
}
If it isn't obvious by now how a std::map<> makes this task easier, it never will be.
check out SEEK_CUR(). If you want to set the cursor back
The problem is a logical one, consider several situations:
Your algorithm does not find the current word. It is inserted at position i of your arrays.
Your algorithm does find the word. The frequency of the word is incremented along with i, which leaves you with blank entries in your arrays whenever there's a word which is already present.
To conclude, 1 works as expected but 2 doesn't.
My advice is that you don't rely on for loops to traverse the string but use a "get-next-until-end" approach which uses a while loop. With this, you can track your next insertion point and thus get rid of the blank entries.
int currentCount = 0;
while (file)
{
// your inner for loop
if (!found)
{
*(words + currentCount) = hold;
*(frequency + currentCount) = 1;
currentCount++;
}
}
Why not use a std::map?
void collect( std::string name, std::map<std::string,int> & freq ){
std::ifstream file;
file.open(name.c_str(), std::ifstream::in );
std::string word;
while( true ){
file >> word; // add toLower
if( file.eof() ) break;
freq[word]++;
}
file.close();
}
The problem with your solution is the use of count in the inner loop where you look for duplicates. You'll need another variable, say nocc, initially 0, used as limit in the inner loop and incremented whenever you add another word that hasn't been seen yet.
lets say that we have:
string list[]= {"12.34.56.78","55.34.5","23.44.5"}
I want the user to enter part of the string which is also a string:
for example string 55 and it will loop through the string a and look for the whole string and print "55.34.5"
What I was doing is:
str is a string input and list is a whole list of the strings
for (int i=0; i<n; i++){
for (int j=0; j<(list[i].length()); j++){
for (int k=0; k<(str.length()); k++){
if (list[i][j] == str[k])
cout<<list[i]<<endl;
else
break;
however, there is a problem with this, and it doesn't work properly.
Update:
so I have updated my code to:
for (int i=0; i<n; i++)
if (strncmp(list[i].c_str(), str.c_str(), str.length()) == 0)){
cout<<list[i]<<endl;
}
however, this doesn't output any of the strings.
For any function fanatics (see it work):
std::string findInList(const std::vector<std::string> &searchFrom, const std::string &lookFor) {
for (const std::string &s : searchFrom) {
if (s.find(lookFor) != std::string::npos)
return s;
}
return "";
}
I used a vector instead of an array because vectors are better and don't require extra work to get the array size from. If C++11 isn't being used, a normal for loop works perfectly fine.
This also assumes you want the first match to be returned. A probably better option is to return a vector of strings, empty if none are found, which makes it explicit that none were found, or as many as are found otherwise. Instead of returning the found string, just add it to the vector and continue on, returning the vector when you're done.
If you want to model the standard algorithms, you can also have it take a beginning iterator and an ending iterator instead of the actual container. This will allow you to call it on any type of container, including arrays, with any range in that container to look through.
Taking both points into consideration, you can evolve it into this (see it work):
template <typename Iterator>
std::vector<std::string> findInList(Iterator start, const Iterator end, const std::string &lookFor) {
std::vector<std::string> ret;
for (; start != end; ++start)
if (start->find(lookFor) != std::string::npos)
ret.emplace_back(*start);
return ret;
}
Again, if not using C++11, emplace_back can be swapped out for push_back.
That just compares the first character in list[i] with the first char in your string. If the corresponding first chars match, it prints the entire ith string and then advances k, the offset into your str, without changing the offset into the string against which you're comparing. I think you can dispense with the inner two loops, and use a fixed length string comparison, i.e.,
for (int i=0; i < n; i++) {
if (strncmp(list[i].c_str(), str.c_str(), str.length()) == 0) {
// match
}
}
Here's an answer that combines both of the previous answers. It uses the find member function of the std::string class
for (int i=0; i < n; i++) {
if (list[i].find(str) != std::string::npos) {
std::cout << list[i] << std::endl;
}
}
I'm trying to loop through a list of strings and find where a given character is located at in said string. I then store the string in a given vector based on where/if the character occurs. I'm getting a runtime error in the following code before the loop finishes executing. I've looked over the it half a dozen times already and can't seem to find anything wrong.
vector< vector<string> > p;
for(list< string >::iterator ix = dictionary.begin(); ix != dictionary.end(); ix++)
{
int index = contains(*ix, guess);
index++;
p.at(index).push_back(*ix); //0 will contain all the words that do not contain the letter
//1 will be the words that start with the char
//2 will be the words that contain the the char as the second letter
//etc...
}
int contains(string str, char c)
{
char *a = (char *)str.c_str();
for(int i = 0; i < (str.size() + 1); i++)
{
if(a[i] == c)
return i;
}
return -1;
}
Change
(str.size() + 1)
...to
str.size()
You would be in undefined territory at str.size(), let alone that PLUS one.
For that matter, why are you fiddling with the extra char* instead of std::string[]?
For THAT matter, why don't you simply use std::string::find()?
That is, of course, assuming you're using std::string and not some other string... :)
In fact, back to the call site... string::find() returns the index of where the target character matched, or string::npos if NOT matched. So, can you dispense with the extra function altogether?
int pos = (*ix).find( guess );
p.at( ( pos == string::npos ) ? 0 : ( pos + 1 ) ).push_back( *ix );
vector< vector > p defines p as empty vector. You must have vector elements added to it before using vector::at().
For example:
const size_t MAX_LETTERS_IN_WORD = 30;
vector< vector<string> > p(MAX_LETTERS_IN_WORD);
/* same as before */
As an alternative you can check p.size() before using at() and push_back() additional elements into p as needed
The problem with the runtime error, might be because you access the vector p at a position that doesn't exist yet. You have to make space in the vector before you access a specific index.