Reputation: 5032
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);
}
Upvotes: 8
Views: 2760
Reputation: 272607
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.
Upvotes: 6
Reputation: 153957
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.
Upvotes: 1
Reputation: 12817
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.
Upvotes: 7
Reputation: 911
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.
Upvotes: 3
Reputation: 10381
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.
Upvotes: 1
Reputation: 91280
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'];
Upvotes: 6
Reputation: 20609
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.
Upvotes: 2
Reputation: 15175
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.
Upvotes: 3