I am working on a "dictionary" for my class. I have an int array called NumOfWordsInFile[] where NumOfWordsInFile[0] corresponds to how many words are in A.txt and NumOfWordsInFile[25] corresponds to Z.txt
As it is now I have a huge switch for the 26 different conditions of letters. I have a function called AddWord(string word). AddWord gets the first letter of the word passed to it and inserts it into the appropriate .txt file. Now here is the problem. Everytime a word is added to A.txt I must increment NumOfWordsInFile[0] by 1. The only way I can think of to do this is with these huge switches. I also have a deleteWord function which conversely decrements NumOfWordsInFile[] if the word is deleted. Now I dont want to have two 26 case swithes but the problem is I dont know how else to do it. Now I could just do the same thing for the delete function but I really dont want to have hundreds of more lines of code to go through. Is there a better way to do this?
Sample of the switch in the AddWord function:
case 'w':
if (numOfWordsInFile[22] < maxWordsPerFile) {
fout.open(fileName.data(), ios::app);
fout << word << " " << endl;
numOfWordsInFile[22]++;
if (totalWordsInDict < maxWordsInDict) {
totalWordsInDict++;
}
return(Dictionary::success);
} else {
return(Dictionary::failure);
}
case 'x':
if (numOfWordsInFile[23] < maxWordsPerFile) {
fout.open(fileName.data(),ios::app);
fout << word << " " << endl;
numOfWordsInFile[23]++;
if (totalWordsInDict < maxWordsInDict) {
totalWordsInDict++;
}
return(Dictionary::success);
} else {
return(Dictionary::failure);
}
Delete function.
bool Dictionary::DeleteAWord(string word)
{
ofstream fout;
ifstream fin;
string x;
string fileName="#.txt";
int count=0;
vector <string> words;
bool deleted=false;
fileName[0]=toupper(word[0]);
fin.open(fileName.data()); //makes the file depending on the first letter of the argument "word"
while (fin >> x)
{
words.push_back(x);
count++;//number of elements in vector
}
if (SearchForWord(x))
{
for ( ;count > 0; count--)
{
if (words[count-1] == word)
{
// cout << "Found word " << word << " during search, now deleting" << endl;
words.erase(words.begin()+(count-1));
deleted = true;
/*
This clearly doesn't work and is what I need help with, I know why it
doesn't work but I don't know how to make it better than having another
huge switch.
*/
numOfWordsInFile[toupper(word[0])]--;
/*
*/
totalWordsInDict--;
fin.close();
}
}
if (deleted)
{
fout.open(fileName.data());
for (int i = 0; i < words.size(); i++)
fout << words[i] << endl;
return(Dictionary::success);
}
return(Dictionary::failure);
}
return(Dictionary::failure);
}
Just taking a very quick look, it seems like you're using the position of the letter in the alphabet to do stuff.
You could replace all your switch statements with one statement that looks like:
int letter = (int)(ActualLetter - 'a');
if(numOfWordsInFile[letter]<maxWordsPerFile){
fout.open(fileName.data(),ios::app);
fout<<word<<" "<<endl;
numOfWordsInFile[letter]++;
if(totalWordsInDict<maxWordsInDict){
totalWordsInDict++;
}
return(Dictionary::success);
}else{
return(Dictionary::failure);
}
ActualLetter is something like, 'a', for example.
On a related note, in the future if you actually have large switch statements, consider encapsulating the code in functions:
switch (letter)
{
case 'a':
LetterA();
break;
case 'b':
LetterB();
break;
...
}
Or even better, you can use polymorphism to have C++ dispatch to the method you want based on the specific derived class:
class BaseLetter
{
...
public:
virtual void DoStuff() = 0;
};
class LetterA : public BaseLetter
{
public:
void DoStuff();
};
class LetterB : public BaseLetter
{
public:
void DoStuff();
};
void Foo(BaseLetter *letter)
{
// Use dynamic dispatch to figure out what to do
letter->DoStuff();
}
Just note, dynamic dispatch does have a (slight) performance hit, and the above is a very bad place to actually use it. The solution I, RedX, and others have posted is much better suited to your specific example.
struct FileInfo {
int NumWords;
std::string Filename;
};
std::map<char, FileInfo> TheFiles;
FileInfo & FI = TheFiles[letter];
// Work with FI.NumWords and FI.Filename
Alternatively:
std::vector<FileInfo> TheFiles;
FileInfo & FI = TheFiles[std::tolower(Letter) - 'a'];
In most practical character encodings that you're likely to encounter whilst using C or C++, 'a' to 'z' are contiguous, so you can get the array index to use simply by doing (c - 'a'), where c is the char you're looking at.
Chars are basically numbers. 'a' is 97, 'b' is 98 and so on.
The easiest way is to simply replace every numOfWordsInFile[n] with numOfWordsInFile[current_char - 'a'] and the whole code repeated for each case may reside in a function, like this:
int AddWord(char current_char) {
if(numOfWordsInFile[current_char - 'a']<maxWordsPerFile){
fout.open(fileName.data(),ios::app);
fout<<word<<" "<<endl;
numOfWordsInFile[current_char - 'a']++;
if(totalWordsInDict<maxWordsInDict){
totalWordsInDict++;
}
return(Dictionary::success);
}else{
return(Dictionary::failure);
}
}
For more general solutions read about hash maps and function pointers (when, for instance, for each char you might want to assign a different function.
if(numOfWordsInFile[letter - 'A']<maxWordsPerFile){
fout.open(fileName.data(),ios::app);
fout<<word<<" "<<endl;
numOfWordsInFile[letter - 'A']++;
if(totalWordsInDict<maxWordsInDict){
totalWordsInDict++;
}
return(Dictionary::success);
}else{
return(Dictionary::failure);
}
This will only work if you only have english letter in your use-case.
Single characters in C++ are really just numbers corresponding to their ASCII values. You can subtract letters from each other to get numerical values. So if word[0] contains the letter A, then word[0] - 'A' will be 0.
So you can index your numOfWordsInFile array directly, and you won't need a switch at all: numOfWordsInFiled[word[0] - 'A'].
Note that 'A' and 'a' have different numeric values, so you'll have to do some extra work if you're mixing upper and lower case.
If your file is A.txt, let your array index be 'A' - 'A' (= 0), if the file is B.txt, let the array index be 'B' - 'A' (= 1), etc.
It depends on how portable you want to be, or how
internationalized. If you can afford to ignore the possibility
that the first letter might be an accented character, and assume
that you're never going to have run on a mainframe, or anywhere
else that uses EBCDIC, then you can convert the first letter to
a specific case, and subtract 'a' or 'A' (depending on the case)
from it to obtain the index. The C++ standard doesn't guarantee
that the letters are contiguous however, and they aren't in
EBCDIC, nor in any of the encodings which support accented
characters. At the very least, you'll have to test that the
first character is a letter, of course.
Handling the internationalization issue is difficult, since
there is no one generally used encoding, and some of the
encodings are multibyte. For the single byte encodings, it's
fairly straight foreward to use a mapping table; a table with
256 entries, indexed by the first letter (cast to unsigned
char), which returns the index into your table. For multibyte
encodings, like UTF-8, the issue is more complicated: you can
translate the initial character in a UTF-8 sequence to an int,
but you can end up with values around a million or more, and you
don't want a table with a million entries (most of which are
completely irrelevant. One simple solution might be to add
a 27th entry for "other". (This would also catch "words" like
"2nd".)
A very portable way to do this would be:
int mappingTable[256];
std::fill_n(mappingTable, 256, 26);
static char const upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ;
static char const lower[] = "abcdefghijklmnopqrstuvwxyz;
for (int i = 0; i < 26; ++ i) {
mappingTable[upper[i]] = i;
mappingTable[lower[i]] = i;
}
Just don't forget to cast the initial character to unsigned char
before indexing.
Related
Here is the code to find the number of matches of a string, which is input from the user, can be found in the file temp.txt. If, for example, we want love to be counted, then matches like love, lovely, beloved should be considered. We also want to count the total number of words in temp.txt file.
I am doing a line by line reading here, not word by word.
Why does the debugging stop at totalwords += counting(line)?
/*this code is not working to count the words*/
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
int totalwords{0};
int counting(string line){
int wordcount{0};
if(line.empty()){
return 1;
}
if(line.find(" ")==string::npos){wordcount++;}
else{
while(line.find(" ")!=string::npos){
int index=0;
index = line.find(" ");
line.erase(0,index);
wordcount++;
}
}
return wordcount;
}
int main() {
ifstream in_file;
in_file.open("temp.txt");
if(!in_file){
cerr<<"PROBLEM OPENING THE FILE"<<endl;
}
string line{};
int counter{0};
string word {};
cout<<"ENTER THE WORD YOU WANT TO COUNT IN THE FILE: ";
cin>>word;
int n {0};
n = ( word.length() - 1 );
while(getline(in_file>>ws,line)){
totalwords += counting(line);
while(line.find(word)!=string::npos){
counter++;
int index{0};
index = line.find(word);
line.erase(0,(index+n));
}
}
cout<<endl;
cout<<counter<<endl;
cout<<totalwords;
return 0;
}
line.erase(0, index); doesn't erase the space, you need
line.erase(0, index + 1);
Your code reveals a few problems...
At very first, counting a single word for an empty line doesn't appear correct to me. Second, erasing again and again from the string is pretty inefficient, with every such operation all of the subsequent characters are copied towards the front. If you indeed wanted to do so you might rather want to search from the end of the string, avoiding that. But you can actually do so without ever modifying the string if you use the second parameter of std::string::find (which defaults to 0, so has been transparent to you...):
int index = line.find(' ' /*, 0*); // first call; 0 is default, thus implicit
index = line.find(' ', index + 1); // subsequent call
Note that using the character overload is more efficient if you search for a single character anyway. However, this variant doesn't consider other whitespace like e. g. tabulators.
Additionally, the variant as posted in the question doesn't consider more than one subsequent whitespace! In your erasing variant – which erases one character too few, by the way – you would need to skip incrementing the word count if you find the space character at index 0.
However I'd go with a totally new approach, looking at each character separately; you need a stateful loop for in that case, though, i.e. you need to remember if you already are within a word or not. It might look e. g. like this:
size_t wordCount = 0; // note: prefer an unsigned type, negative values
// are meaningless anyway
// size_t is especially fine as it is guaranteed to be
// large enough to hold any count the string might ever
// contain characters
bool inWord = false;
for(char c : line)
{
if(isspace(static_cast<unsigned char>(c)))
// you can check for *any* white space that way...
// note the cast to unsigned, which is necessary as isspace accepts
// an int and a bare char *might* be signed, thus result in negative
// values
{
// no word any more...
inWord = false;
}
else if(inWord)
{
// well, nothing to do, we already discovered a word earlier!
//
// as we actually don't do anything here you might just skip
// this block and check for the opposite: if(!inWord)
}
else
{
// OK, this is the start of a word!
// so now we need to count a new one!
++wordCount;
inWord = true;
}
}
Now you might want to break words at punctuation characters as well, so you might actually want to check for:
if(isspace(static_cast<unsigned char>(c)) || ispunct(static_cast<unsigned char>(c))
A bit shorter is the following variant:
if(/* space or punctuation */)
{
inWord = false;
}
else
{
wordCount += inWord; // adds 0 or 1 depending on the value
inWord = false;
}
Finally: All code is written freely, thus unchecked – if you find a bug, please fix yourself...
debugging getting stopped abruptly
Does debugging indeed stop at the indicated line? I observed instead that the program hangs within the while loop in counting. You may make this visible by inserting an indicator output (marked by HERE in following code):
int counting(string line){
int wordcount{0};
if(line.empty()){
return 1;
}
if(line.find(" ")==string::npos){wordcount++;}
else{
while(line.find(" ")!=string::npos){
int index=0;
index = line.find(" ");
line.erase(0,index);
cout << '.'; // <--- HERE: indicator output
wordcount++;
}
}
return wordcount;
}
As Jarod42 pointed out, the erase call you are using misses the space itself. That's why you are finding spaces and “counting words” forever.
There is also an obvious misconception about words and separators of words visible in your code:
empty lines don't contain words
consecutive spaces don't indicate words
words may be separated by non-spaces (parentheses for example)
Finally, as already mentioned: if the problem is about counting total words, it's not necessary to discuss the other parts. And after the test (see HERE) above, it also appears to be independent on file input. So your code could be reduced to something like this:
#include <iostream>
#include <string>
int counting(std::string line) {
int wordcount = 0;
if (line.empty()) {
return 1;
}
if (line.find(" ") == std::string::npos) {
wordcount++;
} else {
while (line.find(" ") != std::string::npos) {
int index = 0;
index = line.find(" ");
line.erase(0, index);
wordcount++;
}
}
return wordcount;
}
int main() {
int totalwords = counting("bla bla");
std::cout << totalwords;
return 0;
}
And in this form, it's much easier to see if it works. We expect to see a 2 as output. To get there, it's possible to try correcting your erase call, but the result would then still be wrong (1) since you are actually counting spaces. So it's better to take the time and carefully read Aconcagua's insightful answer.
I need a bit of guidance.
I want to sharpen my skill, so I'm practicing with smaller projects.
The current challenge is to create a function that can count syllables in a user inputted word.
My thinking is to declare an array of vowels: a, e, i, o, u.
then iterate through the user inputted word, checking if any letters of the word match with the vowel array, and if yes, check if the next letter doesn't. (I'm assuming a syllable is defined by the presence of a vowel AND consonant.)
This will need nested for loops, one to iterate through the word, and then another to iterate through the vowel array with the current index of 'word'.
I haven't even figured out how I'm going to do the same for word[i+1] yet.
But I'm struggling because I can't get my program to compile due to basic errors. I should probably use a string class, but I don't know.
Here's what I've got (And it won't compile!):
#include <iostream>
char vowels[] = {'a', 'e', 'i', 'o', 'u'};
int numberOfSyllables(char *word)
{
int numberOfVowelsFound = 0;
for ( &element : word )
{
bool vowelMatch = 0;
for ( &vowel : vowels)
{
if (element == vowel)
{
vowelMatch = 1;
break;
}
}
if ((vowelMatch == 1) numberOfVowelsFound++;
}
return numberOfVowelsFound;
}
int main()
{
char *userInput[50];
std::cout << "Enter a word: ";
std::cin >> *userInput;
std::cout << numberOfSyllables(userInput) << " syllables found";
return 0;
}
This is not a code review website, but I will try my best anyway:
Your for loops don't have types:
for (TYPE &element : word )
The type you want to loop over in this case is char.
if you wanted the compiler to figure out the type for you:
for (auto &element : word)
You are looping over word with a "foreach" style loop, but a char * cannot be looped over this way. To be precise, there is no std::begin and std::end functions defined for char *, so the "foreach" style loop doesn't know where you want your string to begin/end. Either use a different style of loop, or use a type that does support "foreach" style loops (such as std::string or C++17's std::string_view).
You added an extra parenthesis ( in the if statement:
// |
// v
if ((vowelMatch == 1) numberOfVowelsFound++;
You declare your userInput variable as an "array of 40 pointers to characters", but you probably want to write "characters" to it, not "pointers to characters". Change it's type to "array of 40 characters".
Similarly, you dereference your userInput variable (probably to avoid a warning), which, because userInput is an "array of 40 (pointers to char)", will return the first, uninitialized, "pointer to char" in that array (*var is the same as var[0] in this case). Just remove the dereference operator * and change the type of the array as suggested above, and std::cin will figure out what to do. Because you (wrongfully) dereferenced userInput already to avoid a warning, std::cin thinks you want to write to the location pointed to by the uninitialized pointer. You have no control over where your program will write too at this point; it might simply crash.
Finally, you once again pass the wrong type into numberOfSyllables(userInput), as mentioned before, userInput is an "array of 40 pointers to char", while your function expects a "pointer of chars". Change the type of userInput to "array of chars", which the compiler can then convert to "pointer of chars".
Final code:
// compile with -std=c++17 for std::string_view, or use another loop style
#include <string_view>
#include <iostream>
char vowels[] = {'a', 'e', 'i', 'o', 'u'};
int numberOfSyllables(char *word)
{
int numberOfVowelsFound = 0;
// `std::string_view` can be used in "foreach" style loops
// we need to use `const char`, since `std::string_view` is a "fake string" and not writable
for (const char &element : std::string_view(word))
// Another loop style (This even works in C):
// for (int i=0; word[i] != '\0'; i++) // While the current element is not NUL
// {
// const char element = word[i]; // Remember the current element
{
bool vowelMatch = 0;
for (const char &vowel : vowels) // Use const here too just for good measure
{
if (element == vowel)
{
vowelMatch = 1;
break;
}
}
if (vowelMatch == 1) numberOfVowelsFound++; // Removed a parenthesis here
}
return numberOfVowelsFound;
}
int main()
{
char userInput[50]; // Changed type of this variable
std::cout << "Enter a word: ";
std::cin >> userInput; // Removed a dereference here
std::cout << numberOfSyllables(userInput) << " syllables found";
return 0;
}
#include<iostream>
#include<string>
#include<array>
#include<locale>
using namespace std;
class endeMachine
{
public:
int findIndex(char letter)
{
int index = 0;
while (letter != alphabet[index])
{
index++;
}//end while letter test
return index;
}//findIndex
string subEncrypt(string clear)
{
string subString = clear;
for (int i = 0; i < clear.length(); i++)
{
subString[i] = substitution[findIndex(clear[i])];
}//end for
return subString;
}//subEncrypt
string transEncrypt(string clear)
{
string subString = clear;
for (int i = 0; i < clear.length(); i++)
{
subString[i] = alphabet[findIndex(clear[i]) + offset];
}//end for
return subString;
}//transEncrypt
private://---------------------------------------------------------
array<char, 26> alphabet = { 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z' };
array<char, 26> substitution = { 'm','l','k','j','i','h','g','f','e','d','c','b','a','z','y','x','w','v','u','t','s','r','q','p','o','n' };
int offset = 3;
};//end endoMachine
int main()
{
endeMachine text;
string clear_text = { "Hello" };
cout << text.subEncrypt(clear_text) << endl;
cout << text.transEncrypt(clear_text) << endl;
cin >> clear_text;
}//end main
So in this program I am trying to eventually get to the point where it can:
Encrypt a string entered by the end user
Choose between a substitution or transposition method for the encryption
Decrypt the string previously encrypted post-entry
Choose between either one of the encryption methods to do so
Decrypt the encrypted string without knowing the method of encryption, therefore generating all possible results
My problem is:
When the input string contains an uppercase letter, the whole program shut downs. However if I do something in line 12 like:
while (tolower(letter) != alphabet[index])
the encryption methods both work, but return the strictly lowercase version of the originally input word, making the input of "Hello" result in:
fibby
knoor
upon output, rather than:
Fibby
Knoor
This means that in some way, I need to check for the capitalization of each letter in the word individually, so when it comes time for output, the corresponding letter of the ciphered string can be capitalized before it is output to the screen.
PLEASE HELP!!!
Your findIndex function will fail if an UPPER case char is passed.
The reason is it uses the constant arrays below:
array<char, 26> alphabet = { 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z' };
array<char, 26> substitution = { 'm','l','k','j','i','h','g','f','e','d','c','b','a','z','y','x','w','v','u','t','s','r','q','p','o','n' };
So it will not find a match for an UPPER case letter, and your while loop means your index value returned will be out of bounds....That is 1 more than your array size.
You can:
Check if char is upper case
convert to lower case
process via your functions as normal
if it WAS uppercase, then set your encrypted char to uppercase
...And repeat in reverse for decryption I guess.
There are ugly and elegant ways to check if a char is uppercase...
Ugly:
Convert to lower case and compare with original..Are they the same? If not, Original is upper case. This is probably more portable across similar languages. tolower function here.
Elegant:
The char values (for normal English) uppercase characters are between and including 65 and 90. You could check for the char value in this range
I was working on a system that split a sentence to a 2D pointer.
I don't wanna use any kind of library or another ways like string, because I want to practice pointers and learn them.
char** sscanf(char* hstring)
{
int count = 0;
char* current = hstring;
while (*current)
{
if (*current == ' ')
{
count++;
}
while (*current == ' ')
{
current++;
}
if (*current)
break;
current++;
}
char** result = new char*[count];
current = hstring;
char* nstr = new char;
int c = 0, i = 0;
while (*current)
{
if (!*current) break;
cout << "t1";
if (*current == ' ')
{
*(++result) = nstr;
nstr = nullptr;
nstr = new char;
}
cout << "t2";
while (*current != '/0' && *current == ' ')
{
current++;
}
cout << "t3";
while (*current != '/0' && *current != ' ')
{
if (!*current) break;
*(++nstr) = *current;
current++;
}
cout << "t4";
*nstr = '/0';
cout << "t5";
}
return result;
}
But it's very strange, sometimes redirects me to
static size_t __CLRCALL_OR_CDECL length(_In_z_ const _Elem * const _First) _NOEXCEPT // strengthened
{ // find length of null-terminated string
return (_CSTD strlen(_First));
}
with error: Acces Violation, other times, choose a random line and call it Acces Breakout(sorry if I spelled wrong)
What I want from you is not to repair my code simply, I want some explanations, because I want to learn this stuff.
First, some advice
I understand that you are making this function as an exercise, but being C++ I'd like to warn you that things like new char*[count] are bad practices and that's why std::vector or std::array were created.
You seem confused about how dynamic allocation works. The statement char* nstr = new char; will create just one byte (char) in heap memory, and nothing is guaranteed to be adjacent to it. This means that ++nstr is a "invalid" operation, I mean, it's making the nstr point to the next byte after the allocated one, which can be some random invalid location.
There is a whole lot of other dangerous operations in your code, like calling new several times (which reserves memory) and not calling delete on them when you no longer use the reserved memory (aka. memory leaks). Having said that, I strongly encourage you to study this subject, for example starting with the ISO C++ FAQ on memory management.
Also, before digging into pointers and dynamic allocation, you should be more confortable with statements and flow control. I say this because I see some clear misunderstandings, like:
while (*current) {
if (!*current) break;
...
}
The check inside the if statement will certainly be false, because the while check is executed just before it and guarantees that the opposite condition is true. This means that this if is never evaluated to true and it's completely useless.
Another remark is: don't name your functions the same as standard libraries ones. sscanf is already taken, choose another (and more meaningful) one. This will save you some headaches in the future; be used to name your own functions properly.
A guided solution
I'm in a good mood, so I'll go through some steps here. Anyway, if someone is looking for an optimized and ready to go solution, see Split a String in C++.
0. Define the steps
Reading your code, I could guess some of your desired steps:
char** split_string(char* sentence)
{
// Count the number of words in the sentence
// Allocate memory for the answer (a 2D buffer)
// Write each word in the output
}
Instead of trying to get them right all at once, why don't you try one by one? (Notice the function's and parameter's names, clearer in my opinion).
1. Count the words
You could start with a simple main(), testing your solution. Here is mine (sorry, I couldn't just adapt yours). For those who are optimization-addicted, this is not an optimized solution, but a simple snippet for the OP.
// I'll be using this header and namespace on the next snippets too.
#include <iostream>
using namespace std;
int main()
{
char sentence[] = " This is my sentence ";
int n_words = 0;
char *p = sentence;
bool was_space = true; // see logic below
// Reading the whole sentence
while (*p) {
// Check if it's a space and advance pointer
bool is_space = (*p++ == ' ');
if (was_space && !is_space)
n_words++; // count as word a 'rising edge'
was_space = is_space;
}
cout << n_words;
}
Test it, make sure you understand why it works. Now, you can move to the next step.
2. Allocate the buffer
Well, you want to allocate one buffer for each word, so we need to know the size of each one of them (I'll not discuss whether or not this is a good approach to the split sentence problem..). This was not calculated on the previous step, so we might do it now.
int main()
{
char sentence[] = " This is my sentence ";
///// Count the number of words in the sentence
int n_words = 0;
char *p = sentence;
bool was_space = true; // see logic below
// Reading the whole sentence
while (*p) {
// Check if it's a space and advance pointer
bool is_space = (*p++ == ' ');
if (was_space && !is_space)
n_words++; // count as word a 'rising edge'
was_space = is_space;
}
///// Allocate memory for the answer (a 2D buffer)
// This is more like C than C++, but you asked for it
char **words = new char*[n_words];
char *ini = sentence; // the initial char of each word
for (int i = 0; i < n_words; ++i) {
while (*ini == ' ') ini++; // search next non-space char
char *end = ini + 1; // pointer to the end of the word
while (*end && *end != ' ') end++; // search for \0 or space
int word_size = end - ini; // find out the word size by address offset
ini = end; // next for-loop iteration starts
// at the next word
words[i] = new char[word_size]; // a whole buffer for one word
cout << i << ": " << word_size << endl; // debugging
}
// Deleting it all, one buffer at a time
for (int i = 0; i < n_words; ++i) {
delete[] words[i]; // delete[] is the syntax to delete an array
}
}
Notice that I'm deleting the allocated buffers inside the main(). When you move this logic to your function, this deallocation will be performed by the caller of the function, since it will probably use the buffers before deleting them.
3. Assigning each word to its buffer
I think you got the idea. Assign the words and move the logic to the separated function. Update your question with a Minimal, Complete, and Verifiable example if you still have troubles.
I know this is a Q&A forum, but I think this is already a healthy answer to the OP and to others that may pass here. Let me know if I should answer differently.
int ascii[1000] = {0};
string *data = (string*)malloc ( 1000*sizeof( string));
char *text = (char*)malloc ( 1000 *sizeof( char));
cout << "Enter the first arrangement of data." << endl;
cin.getline(text, 1000);
char *token = strtok(text, " ");
while ( token != NULL )
{
if ( strlen(token) > 0)
{
cout << "The tokens are: " << token << endl;
data[Tcount++] = *token;
}
token = strtok(NULL, " ");
for(i=0; i < (Tcount); i++)
{
ascii[i] = (int)data[i]; // error here
}
Im using this code to build a parser and i want to store the ascii values of the tokens which are stored in 'data' into an array named 'ascii'.
When i run the program i get the error message, "error: assigning to 'int' from incompatible type 'string' (aka 'basic_string, allocator >')
Any help would be appreciated.
One thing before the main event here. Obviously you're allowed to use std::string so, let's get the data in a more civilized fashion.
std::vector<std::string> data;
std::string line;
std::getline(cin, line); //read a whole line
std::stringstream tokenizer(line); // stuff the line into an object that's
// really good at tokenizing
std::string token;
while (tokenizer >> token) // one by one push a word out of the tokenizer
{
data.push_back(token); //and stuff it into a vector
}
we now have all of the individual words on the line packed into a nice resizable container, a vector. No messy dynamic memory to clean up.
Step 2: turn those strings into ints. 'Fraid you can't do that, ace. You could take a string that represents a number and turn it into an int. That's easy. Dozens of ways to do it. I like strtol.
But the ascii values are character by character. A string is a variable number of characters. You can pack one into an int, shift the int over by the width of one character and stuff in another, but you're going to run out of space after probably 4 or 8 characters.
Let's go with that, shall we? And we'll do it the old way without an iterator.
std::string data;
int ascii = 0;
if (data.length() > 0)
{
ascii |= data[index];
for(size_t index = 0; index < data.length(); index++)
{
ascii <<= 8; //we're talking ascii here so no unicode bit counting games
ascii |= data[index];
}
}
Done. Not very useful unless all the strings are pretty short, but done.
Instead if you're going to do a parser why not go full geek and try this:
typedef void handlerfunc();
std::map<std::string, handlerfunc> parser;
parser["do something"] = somethingfunc;
parser["do something else"] = somethingelsefunc;
Where somethingfunc is a function that looks like void somethingfunc() that, obviously, does something. Dito somethingelsefunc. Only it does somethingelse.
Usage could be as simple as:
parser[token]();
But it's not. Sigh.
It's more like
found = parser.find(token)
if (found != parser.end())
{
found->second();
return CMD_OK;
}
else
{
return CMD_NOT_FOUND;
}
But seriously, look into some of the fun stuff a good container can do for you. Save a ton of time.
I crapped out all of the code without a compiler. Please let me know if I borked any of it.