Trying to read a single character at a time into an array of indefinite size - c++

I am a CS student working on a c++ project. We have been instructed to declare a struct and use it to read in an array of chars and keep a tally of how many letters are used in the string. We are not allowed to use a string; it MUST be an array of our declared struct.
The input must be as long as the user wants; the code has to be able to accept new lines of input and be terminated by '.'
I'm really struggling here. I don't even know where to begin. I've thrown together some code as best-guess for what to do, but it crashes after pressing "." then enter, and I don't know why.
//declare struct
struct data
{
int tally = 0;
char letter;
};
//size of string to read in at a time
const int SIZE_OF_CHUNK = 11;
int main()
{
//input chunk of struct
data input[SIZE_OF_CHUNK];
int placemark,
length;
cout << "Enter sequence of characters, '.' to terminate:" << endl;
do
{
for (int index = 0; (input[index].letter != '\0') && (input[index - 1].letter != '.'); index++)
{
cin >> input[index].letter;
placemark++;
}
//I intend to put something here to handle if the code
needs to read in another chunk, but I want to fix the crashing
problem first
}
while (input[placemark].letter != '.');
//print out what was read in, just to check
for (int index = 0; input[index].letter != '\0'; index++)
{
cout << input[index].letter;
}
return 0;
}
I've tried looking up how to read in a single character but haven't found anything helpful to my circumstances so far. Any tips on what I'm doing wrong, or where I can find helpful resources, would be very much appreciated.

Are you sure you must use a declared struct?
If you just want to count the number of times a character has appeared, you don't need to store the character; you just need to store the number of times it appeared. So just unsigned lettersCount[26], and each index maps to a letter (i.e. index 0 means a, index 1 means b). Whenever a letter appears, just increase the count of that index.
You can map a letter to the index by making use of ASCII. Every letter is represented by a decimal number that you can look it up at ASCII table. For example, the letter a is represented by the decimal value 97, b is 98 and so on. The number increases successively, which we can make use of. So if you want to map a letter to an index, all you need to do is just value - 97 or value - 'a'. For example, if you read in the letter a, take away 97 from that and you'll get 0, which is what you want. After getting the index, it's just a simple ++ to increment the count of that letter.
Regarding the treatment of uppercase and lowercase (i.e. treat them the same or differently), it'll be up to you to figure it out how to do it (which should be fairly simple if you can understand what I've explained).

Related

Creating an array of characters (user-input) through recursion

I'm creating a program (whose functions must be recursive) which accepts a series of characters from the user, terminated by a period, and displays the characters backwards on the screen. A sample run should give:
Enter a character: H
Enter a character: i
Enter a character: .
iH
I made a function that reverses an array which takes a character array and its size as parameters.
void print_backwards(char a[], int size){
int i = size-1;
cout << a[i];
i--;
if(i >= 0){
print_backwards(a, i);
}
}
I have a problem, however, with initializing the character array using recursion. How can I solve this problem?
This sounds like a homework problem, so I'm not going to solve the problem for you. However, I can give you a push in the right direction.
Make a function that takes a character array by reference and an offset.
On the first call of the function, give it an offset of zero and pass it the empty array.
The function itself should read a single character from the user.
If the character is a ., write a null byte to the current position and return the current offset, else, write the supplied character to the current position, then call the function again with offset+1.
The function will return the length of the input.
The function body will look something like
// Get character from user
if ( c == '.' ) {
input[offset] = '\0';
return offset;
} else {
input[offset] = c;
return thisFunction(input, offset+1);
}

Line Breaks when reading an input file by character in C++

Ok, just to be up front, this IS homework, but it isn't due for another week, and I'm not entirely sure the final details of the assignment. Long story short, without knowing what concepts he'll introduce in class, I decided to take a crack at the assignment, but I've run into a problem. Part of what I need to do for the homework is read individual characters from an input file, and then, given the character's position within its containing word, repeat the character across the screen. The problem I'm having is, the words in the text file are single words, each on a different line in the file. Since I'm not sure we'll get to use <string> for this assignment, I was wondering if there is any way to identify the end of the line without using <string>.
Right now, I'm using a simple ifstream fin; to pull the chars out. I just can't figure out how to get it to recognize the end of one word and the beginning of another. For the sake of including code, the following is all that I've got so far. I was hoping it would display some sort of endl character, but it just prints all the words out run together style.
ifstream fin;
char charIn;
fin.open("Animals.dat");
fin >> charIn;
while(!fin.eof()){
cout << charIn;
fin >> charIn;
}
A few things I forgot to include originally:
I must process each character as it is input (my loop to print it out needs to run before I read in the next char and increase my counter). Also, the length of the words in 'Animals.dat' vary which keeps me from being able to just set a number of iterations. We also haven't covered fin.get() or .getline() so those are off limits as well.
Honestly, I can't imagine this is impossible, but given the restraints, if it is, I'm not too upset. I mostly thought it was a fun problem to sit on for a while.
Why not use an array of chars? You can try it as follow:
#define MAX_WORD_NUM 20
#define MAX_STR_LEN 40 //I think 40 is big enough to hold one word.
char words[MAX_WROD_NUM][MAX_STR_LEN];
Then you can input a word to the words.
cin >> words[i];
The >> operator ignores whitespace, so you'll never get the newline character. You can use c-strings (arrays of characters) even if the <string> class is not allowed:
ifstream fin;
char animal[64];
fin.open("Animals.dat");
while(fin >> animal) {
cout << animal << endl;
}
When reading characters from a c-string (which is what animal is above), the last character is always 0, sometimes represented '\0' or NULL. This is what you check for when iterating over characters in a word. For example:
c = animal[0];
for(int i = 1; c != 0 && i < 64; i++)
{
// do something with c
c = animal[i];
}

Incrementing characters being read

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
*/
}
}

ASCII and isalpha if statement issue

I am writing a program that takes a user inputted character, such as A, and a user inputted number, such as 7. The program checks the validity of the character, if true runs thru till it gets to this loop inside of a function. I am using ascii decimal for this loop inside of a function. This loop needs to check isalpha and if it is run the code inside the {}'s, it's doing that correctly. The else is not working the way I want and am not sure how to correct it. I need the else (is not alpha) to add a 1 back to the counter in the loop and increase the ascii by 1. If I run it as so, it gives off a retry/ignore/abort error. If I run it without the num++; it runs and stops after the loop ends. So, if you put in a Z and choose 3, it runs thru the loop 3 times and outputs just a Z. Any thoughts on how to fix this?
I need it to output something like: Input: Z Input: 4 it should output: Z A B C to the screen. It needs to ignore other ascii non alpha characters.
Thanks
string buildSeries(char A, int num)
{
//builds the output with the info the
//user inputted
stringstream str1;
string outted;
int DeC=(int)A, i = 0;
//loop builds the output
for(i=0;i<num;i++)
{
if (isalpha(DeC))
{
//converts the decimal to a letter
str1<<(char)DeC;
//adds a space
str1<<" ";
//increases the decimal
DeC++;
}
else
{
num++;
DeC++;
}
}
//builds the sstream and puts it in
//variable "outted"
outted = str1.str();
return outted;
}
If you need to loop back to 'A' at Z change your DeC++ to
if DecC == 'Z'
DecC = 'A'
else
DecC++;
Or you could get fancy and use the modulus operator
Edit
I think the problem may be that this stringstream insertion operator, >>, doesn't have an overload that handles a char. It's converting the char to a short or an int then inserting it. Try using string::append(size_t size, char c) instead. That should handle inserting a char.
That is replace you calls to str1<<(char)DeC; with outted.append(1, (char)DeC) and remove your use of the string stream
What is DeC? The phrase "ascii list" makes me suspect it's a 'C' string, in which case you are calling isAlpha() on the pointer not on the value in the string.
edit: If for example you have
char DeC[40];
// read in a string form somewhere
// DeC is a pointer to some memory it has a value of a 32 or 64bit number
if ( isAlpha(DeC) {
// what you might have meant is
if ( isAlpha(*DeC) { // the character value at the current position in DeC

C++ Sample Project - Need help with algorithm

I had one of my friends send me this programming assignment so I could brush up on some of my C++ skills. Below is the program description and my proposed algorithm. Could someone please provide some feedback/alternative solutions:
Problem:
This program creates word search puzzles - Words are printed at random locations in a rectangular grid. Words may be horizontal or vertical and may be forward (left-to-right or top-to-bottom) or reversed (right-to-left or bottom-to-top). Unused grid squares are filled with random letters. The program should take a set of word lists as input and produce two files as output. The first lists, for each puzzle, the list of words in the puzzle, followed by the puzzle itself. The second should show where in each puzzle the words are located, without the random filler letters
Our input file contains the following:
A number n > 0 (representing the # of words in the puzzle) followed by that many words. For example:
3
FRODO
GIMLI
ARAGORN
N will not be larger than 10
We need to create the puzzle using a multidimensional array of size 12 x 12
Requirements:
1. Two output files - One containing the puzzle words and he puzzles, one with only the solutions and no filler characters
2.There needs to be as many horizontal words as there are vertical words
3. A 1/3 of the words needs to be reversed
4. There needs to be at least two intersections in the puzzle
Proposed Algorithm:
1. Create two multidimensional arrays - one for the puzzle and one for the solutions
2. Create a one-dimensional array that contains the various letters of the alphabet
3. Fill the puzzles array with random letters of the alphabet (using a pseudo-random # generator and the array from step # 2)
4. Begin reading the input file
5. Read in n
6. While counter is less than n, read in words, also have a counter for vertical words and horizontal words
7. For each word, find the length of the string
8. Find a random array location to insert the word.
9. If random location index + string length <= 12 or if random location index - string length is >= 0 (to ensure that the word will fit forward or reverse) insert the word
10. Also insert the word into the solutions array
12. Reuse arrays to insert all the words in the input file (in a similar manner)
I am still unsure as to how I can ensure that at least two intersections exist.
I am also concerned that my proposed algorithm is unnecessarily convoluted.
Your feedback is greatly appreciated!
OK, Here's as far as I got into the coding process, before I decided to go back and revisit the algorithm:
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <ctime>
using namespace std;
//Error Codes
const int INPUT_FAIL = 1;
const int PUZZLES_OUTPUT_FAIL = 2;
const int SOLUTIONS_OUTPUT_FAIL = 3;
//Function Declarations/Prototypes
void OpenFiles(ifstream& input, ofstream& puzzles, ofstream& solutions);
//PRE: The filestream objects exist and their address locations are passed in
//POST: The filestreams are opened. If they cannot be opened, an error message is printed to screen
// and the program is terminated.
void FillArray(char puzzle[][12], char alphabet[]);
//PRE: The address of the array is passed in
//POST: The array is filled with a random set of
void CreatePuzzle(char puzzle[][12], ifstream& input, ofstream& puzzles, ofstream& solutions);
//PRE: The address of the puzzle array,the address of the ifstream object and the addresses of the
// ofstream objects are passed in.
//POST: The data in the input file is read and the words are input into the puzzle AND the puzzle
// and solutions are printed to file.
void PrintPuzzle(char puzzle[][12], ofstream& output);
//PRE: The address of the puzzle array and the ofstream object is passed in
//POST: The puzzle is output to the file
int main()
{
//Seed the pseudo random generator
srand(time(NULL));
//Declare the filestream objects
ifstream input;
ofstream puzzles, solutions;
//Declare the 2D array
char puzzle[12][12];
char solution[12][12];
//Declare an alphabet array
char alphabet[27] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
/*char alphabet[27] = {'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'};*/
//Attempt to open files
OpenFiles(input, puzzles, solutions);
//Fill puzzle array with random letters of the alphabet
FillArray(puzzle, alphabet);
//Print puzzle
PrintPuzzle(puzzle, puzzles);
//Read in data to create puzzle
input >> numwords;
return 0;
}
//Function definitions
void OpenFiles(ifstream& input, ofstream& puzzles, ofstream& solutions)
{
//Attempt to open files
input.open("input.txt");
puzzles.open("puzzles2.txt");
solutions.open("solutions2.txt");
//Ensure they opened correctly
if (input.fail())
{
cout << "Input file failed to open!" << endl;
exit(INPUT_FAIL);
}
if (puzzles.fail())
{
cout << "Output file - puzzles.txt failed to open!" << endl;
exit(PUZZLES_OUTPUT_FAIL);
}
if (solutions.fail())
{
cout << "Output file - solutions.txt failed to open" << endl;
exit(SOLUTIONS_OUTPUT_FAIL);
}
}
void FillArray(char puzzle[][12], char alphabet[])
{
int tmp;
for(int i = 0; i < 12; i++)
{
for(int j = 0; j < 12; j++)
{
tmp = rand()%26;
puzzle[i][j] = alphabet[tmp];
}
}
}
void PrintPuzzle(char puzzle[][12], ofstream& output)
{
for(int i = 0; i < 12; i++)
{
for(int j = 0; j < 12; j++)
{
output << puzzle[i][j] << " ";
}
output << endl;
}
}
void CreatePuzzle(char puzzle[][12], ifstream& input, ofstream& puzzles, ofstream& solutions)
{
string pword; //#the puzzle word being read
int numwords; //# of words in a given puzzle
char tmparray[13];
int wordlength = 0;
int startloc;
//Read the number of words to be used in the puzzle
input >> numwords;
int vwords = numwords/2; //#of vertical words
int rwords = numwords/3; //# of reversed words
int hwords = (numwords - (numwords/2)); //# of horizontal words
for(int i = 0; i < numwords; i++)
{
//Read the word into our tmparray
input >> pword;
tmparray[] = pword;
wordlength = pword.length();
//Find a random array location to begin inserting the words
startloc = rand()%12;
int tmpcount = 0; //a temporary counter to ensure that
for(tmpcount; tmpcount <= 1; tmpcount ++)startloc + wordlength < 12)
{
for(int j = 0; j <= wordlength; j++)
{
puzzle[startloc][startloc]
Try it on paper first
Then make it work (in code)
Then make it fast /efficent / elegant
edit - Sorry I wasn't being sarcastic, this was before the OP posted code, and it wasn't clear they had attempted the problem.
My first suggestion would be don't bother pre-filling the array with anything - just stick th words in, and fill in the gaps randomly once you're finished.
A few thoughts/suggestions:
I think you could do this with one single 2D array instead of two. It seems simpler in my mind, though of course you may find I'm wrong when you sit down to actually implement this.
At step 9, instead of finding a spot where the word could fit either forward or reversed, first decide which way it's going to go in (perhaps using a random number generator). Then pick a spot and check the first condition, and just insert the word, either forwards or backwards. Remember this is a 2D array though, so you'll need to look at both the x and y coordinates of the point you chose. You'll also have to look at the words you've already placed in the grid, to make sure you don't overwrite something that's already there.
For finding intersections, think about how you would do it by hand (like Martin said). You've already placed a few words in the grid and now you want to add a new one. Suppose there aren't many intersections yet so you'd like to have the current word intersect one of the ones already in teh grid, if possible. How do you know if an intersection is possible, and how do you know where to place the word so that it creates an intersection?
My first thoughts are:
Place the words first and then randomly fill the gaps. I think it will be much easier for you to visualise it that way and also be easier to check if the word placement is done correctly.
After placing the first word I would save the word into an array. After checking if the second word is small enough to fit the puzzle I would have the program find the common letters of word 1 and 2. If they have a common letter, place the second word so the two words intersect(of course you must first check if the way you try to place word 2 is legal aka if it fits the puzzle the way you are trying to place it). Word 3 is the same way except look for possible intersections between words 1 and 2.If there is no possible intersection try the next word. If you can't have 2 or more intersections after placing all words, empty the array and replace the first word in a different random position. After having placed words in such a way that at least two intersections exist, you can continue and place the rest of the words.