I am writing a program that counts the amount of letters and words in a string given by the user. For some reason, the number of words is being added to the number of letters. If there is 3 words in the sentence and 12 letters, then it says that there is 15 words. My code is below:
#include <cs50.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
int storeLetters[] = {};
int storeWords[] = {};
// declare functions
int count_letters(string text);
int count_words(string text);
int main(void)
{
// ask user for text passage
string text = get_string("Text: ");
int numOfLetters = count_letters(text);
printf("%d",numOfLetters);
printf(" letters\n");
int numOfWords = count_words(text);
printf("%d",numOfWords);
printf(" words\n");
}
int count_letters(string text)
{
int amountOfLetters = 0;
for (int i = 0, n = strlen(text); i < n; i++)
{
if (isalpha(text[i]))
{
storeLetters[i] += 1;
amountOfLetters += storeLetters[i];
}
else
{
storeLetters[i] += 0;
amountOfLetters += storeLetters[i];
}
}
return amountOfLetters;
}
int count_words(string text)
{
int amountOfWords = 0;
for (int x = 0, n = strlen(text); x < n; x++)
{
if (text[x] == '?' || text[x] == '!' || text[x] == '.' || text[x] == ' ')
{
storeWords[x] += 1;
amountOfWords += storeWords[x];
}
else
{
storeWords[x] += 0;
amountOfWords += storeWords[x];
}
}
return amountOfWords;
}
storeLetters and storeWords are arrays, which you initialize to zero length. If you access storeLetters[0] (or any other index), you go past the end. It's not gonna work.
You don't need those variables at all. Just increment amountOfLetters directly.
int count_letters(string text)
{
int amountOfLetters = 0;
for (int i = 0, n = strlen(text); i < n; i++)
{
if (isalpha(text[i]))
{
amountOfLetters += 1;
}
}
return amountOfLetters;
}
I failed to pass the Valgrind tests and couldn't figure out what went wrong with my code. It seems like the issue is in the load() function as the Valgrind tests pointed out at the malloc() line. Could anyone help me take a look? Any guidance would be appreciated. Thank you!
Here is my code:
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include "dictionary.h"
// Represents a node in a hash table
typedef struct node
{
char word[LENGTH + 1];
struct node *next;
}
node;
// TODO: Choose number of buckets in hash table
const unsigned int N = 100;
// Hash table
node *table[N];
int count =0;
// Returns true if word is in dictionary, else false
bool check(const char *word)
{
// TODO
int i = hash(word);
node *cursor = table[i];
if (table[i] == NULL)
{
return false;
}
else
{
while(cursor!= NULL)
{
if(strcasecmp(cursor->word, word) == 0)
{
return true;
}
else
{
cursor = cursor->next;
}
}
}
return false;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
// TODO: Improve this hash function
int bucket;
if(word[1] != 0)
{
bucket = (((toupper(word[0])-'A') * (toupper(word[1]- 'A')))% 10 + (toupper(word[0])-'A'));
}
else
{
bucket = (((toupper(word[0])-'A') * (toupper(word[0])-'A'))%10 + (toupper(word[0])-'A'));
}
return bucket;
}
// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
// TODO 1
//open the dictionary
FILE *file = fopen(dictionary, "r");
if(file == NULL)
{
printf("Can't load the dictionary\n");
return false;
}
//read string from file one at a time
char word[LENGTH + 1];
for (int i=0; i < N; i++)
{
table[i] = NULL;
}
while(fscanf(file, "%s", word) != EOF)
{
node *n = malloc(sizeof(node));
//create a new node for each word
if(n == NULL)
{
unload();
return false;
}
strcpy(n->word, word);
n->next = NULL;
count++;
char *c = n->word;
int number = hash(c);
if (table[number] != NULL)
{
//point the new node to the first node existing in the table
n->next = table[number];
//point the header to the new node
table[number] = n;
}
else
{
//n->next = NULL;
table[number] = n;
}
}
fclose(file);
return true;
}
// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
// TODO
return count;
//return 0;
}
// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
for (int i = 0; i > N; i++)
{
node *cursor = table[i];
while(cursor != NULL)
{
node *tmp = cursor;
cursor = cursor->next;
free(tmp);
}
free(cursor);
}
// TODO
return true;
}
Here is what the Valgrind tests show:
Valgrind tests
c.99 is this line -> node *n = malloc(sizeof(node));
The problem is in unload. It doesn't free any nodes. Review this line carefully and critically, it contains the error.
for (int i = 0; i > N; i++)
After running my code through help50 Valgrind, I got the following error message:
==6830== Invalid read of size 1
==6830== at 0x4C33614: strcasecmp (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6830== by 0x401176: check (dictionary.c:52)
==6830== by 0x400CD9: main (speller.c:112)
Looks like you're trying to access 1 byte of memory that isn't yours? Did you try to index into an array beyond its bounds? Take a closer look at line 52 of dictionary.c.
I think it has something to do with my check function but line 52 is just an if statement and I can't figure out where I'm trying to access that 1 byte from.**
My code is below:
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <strings.h>
#include "dictionary.h"
// Represents a node in a hash table
typedef struct node
{
char word[LENGTH + 1];
struct node *next;
}
node;
// Number of buckets in hash table
const unsigned int N = 1000;
//Number of words
unsigned int noWords = 0;
//Variable to check if dictionary loaded
bool isLoaded = false;
// Hash table
node *table[N];
// Returns true if word is in dictionary else false
bool check(const char *word)
{
//Changing letters to lower case because case insensitive
//Copy created because word argument is a constant. copy can be edited
int n = strlen(word) + 1;
char copy[LENGTH + 1];
for (int i = 0; i < n; i++)
{
copy[i] = tolower(word[i]);
}
// Add null terminator to end string
copy[n] = '\0';
//Hash the word to convert it to index and check if it's in any of the linked lists
int index = hash(copy);
if (table[index] != NULL) //Check if word is in linked list
{
for (node *cursor = table[index]; cursor != NULL; cursor = cursor -> next)
{
if (strcasecmp(cursor -> word, copy) == 0)
{
return true;
}
}
}
return false;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
//Taken from http://www.cse.yorku.ca/~oz/hash.html (by djb2)
unsigned long h = 5381;
int c;
while ((c = *word++))
{
c = tolower(c);
h = (((h << 5) + h) + c) % N; /* hash * 33 + c*/
}
return h;
}
// Loads dictionary into memory, returning true if successful else false
bool load(const char *dictionary)
{
char word[LENGTH + 1];
//Open dictionary
FILE *f = fopen(dictionary, "r");
//Check if file can be opened
if (f == NULL)
{
printf("%s\n", "File cannot be opened!");
return false;
}
//Read strings from file
while (fscanf(f, "%s", word) != EOF)
{
noWords++;
node *newNodePointer = malloc(sizeof(node));
if (newNodePointer == NULL)
{
unload();
printf("Out of memory");
return false;
}
int index = hash(word);//hashtable is an array of linked list. index helps insert node into hashtable
strcpy(newNodePointer -> word, word);//Copies word from infile into new node's word field
if (table[index] == NULL)//Check if same word already exists in the bucket
{
newNodePointer -> next = NULL;
table[index] = newNodePointer;
}
else
{
newNodePointer -> next = table[index];
table[index] = newNodePointer;
}
free(newNodePointer);
}
fclose(f);
isLoaded = true;
return true;
}
// Returns number of words in dictionary if loaded else 0 if not yet loaded
unsigned int size(void)
{
if (isLoaded)
{
return noWords;
}
return 0;
}
// Unloads dictionary from memory, returning true if successful else false
bool unload(void)
{
//Check if there's even a loaded dictionary
if (!isLoaded)
{
return false;
}
//Iterate through hashtable
for (int i = 0; i < N; i++)
{
if (table[i] != NULL)
{
node *cursor = table[i];
while (cursor != NULL)
{
node *tmp = table[i]; //tmp pointer continues pointing at table[i] while cursor points at next item in hashtable
cursor = cursor -> next;
free(tmp);
}
}
}
return true;
}
The problem is from here in load: free(newNodePointer);. It just released the memory where the word and the next pointer are stored!
I have to use a lexical analyzer to compare 2 lines of a text file, if the 2 lines are identical I merge them this way:
STATE a TO a b b b OUT 0 1 1 0
STATE b TO a b b b OUT 0 1 1 0
STATE ab TO a b b b OUT 0 1 1 0
Current_State = a
Next_States = a b b b
Outputs = 0 1 1 0
I've ignored in the text file everything that comes before the state,
my code is as follows :
#pragma warning(disable:4786)
#include <string>
#include <map>
#include <vector>
#include <iostream>
#include <stdio.h>
using namespace std;
#define Getc(s) getc(s)
#define Ungetc(c) {ungetc(c,IP_Input); ugetflag=1;}
typedef vector<string> list; // Um Eingänge ausgänge zu speichern
list next_states;
list Outputs;
int tok = 0;
/*
* Lexical analyzer states.
*/
enum parcases { // Case that can occur C_.....
c_SkipBegin, // When State is found go to DEFCURRENTSTATE
c_DEFCURRENTSTATE, // DEFCURRENTSTATE occurs scan the states
c_DEFNEXTSTATES, // DEFNEXTSTATES occurs scan the next states
c_DEFOUT, //DEFOUT occurs scan the outputs
c_FINISHED
};
enum lexstate {
L_START, L_INT, L_IDENT, L_STRING, L_STRING2,
L_COMMENT, L_TEXT_COMMENT, L_LINE_COMMENT, L_END_TEXT_COMMENT
};
const int STRING1 = 3;
const int IDENTIFIER = 4;
const int INTEGER1 = 5;
const int TOKENSTART = 300;
class CParser
{
public:
string yytext; //input buffer
struct tyylval { //value return structure
string s; //string
int i; //integer
}yylval;
FILE* IP_Input; //Input File
FILE* IP_Error; //Error Output
FILE* IP_List; //List Output
int IP_LineNumber; //Line counter
int ugetflag; //checks ungets
int prflag; //controls printing
map<string, int> IP_Token_table; //Tokendefinitions
map<int, string> IP_revToken_table; //reverse Tokendefinitions
string current_states; // Vector um scanned_states zu speichern
list first_lines;
list next_states; // Vector um scanned_inputs zu speichern
list Outputs; // Vector um scanned_outputs zu speichern
int yylex(); //lexical analyser
void yyerror(char* ers); //error reporter
int IP_MatchToken(string& tok); //checks the token
void InitParse(FILE* inp, FILE* err, FILE* lst); //Sets the File pointers
int yyparse(); //parser
list fusion_lines(list& list_one, list& list_two);
bool compare(list& list_one, list& list_two);
void pr_tokentable(); //test output for tokens
void IP_init_token_table(); //loads the tokens
void Load_tokenentry(string str, int index); //load one token
void PushString(char c); //Used for dtring assembly
parcases SkipTillBegin(int tok, list& first_lines);// Skip till begin is found
parcases ScanTheCurrentState(int tok, string& current_states); // Scan the STATE
parcases ScanTheNextStates(int tok, list& next_states); // Scan the TO STATE
parcases ScanTheOutputs(int tok, list& Outputs); // Scan the Outputs
CParser() { IP_LineNumber = 1; ugetflag = 0; prflag = 0; }; //Constructor
};
//------------------------------------------------------------------------
// Adds a character to the string value
void CParser::PushString(char c)
{
yylval.s += c;
}
//------------------------------------------------------------------------
void CParser::Load_tokenentry(string str, int index)
{
IP_Token_table[str] = index;
IP_revToken_table[index] = str;
}
void CParser::IP_init_token_table()
{
Load_tokenentry("STRING1", 3);
Load_tokenentry("IDENTIFIER", 4);
Load_tokenentry("INTEGER1", 5);
int ii = TOKENSTART;
Load_tokenentry("AND", ii++);
Load_tokenentry("OR", ii++);
Load_tokenentry("Begin", ii++);
Load_tokenentry("End", ii++);
}
//------------------------------------------------------------------------
void CParser::pr_tokentable()
{
typedef map<string, int>::const_iterator CI;
const char* buf;
printf("Symbol Table ---------------------------------------------\n");
for (CI p = IP_Token_table.begin(); p != IP_Token_table.end(); ++p) {
buf = p->first.c_str();
printf(" key:%s val:%d\n ", buf, p->second);
}
}
int CParser::yyparse()
{
string Current_state;
list given_first_lines;
list given_Next_states;
list given_outputs;
// Define the first state first we read the file till begin Occur what is befor "State" is ignored.
parcases Case = c_SkipBegin;
printf(" %s ", first_lines, "/ln");
while ((tok = yylex()) != IP_Token_table["End"])
{
if (Case == c_SkipBegin)
{
Case = SkipTillBegin(tok, first_lines);
}
else if (Case == c_DEFCURRENTSTATE)
{
}
}
return 0;
}
//------------------------------------------------------------------------
parcases CParser::SkipTillBegin(int tok, list& first_lines)
{
parcases retval;
if (tok != IP_Token_table["STATE"])
{
first_lines.push_back(string(yylval.s)); // the 2 IN lines
retval = c_SkipBegin;
}
else {
retval = c_DEFCURRENTSTATE;
}
return retval;
}
parcases CParser::ScanTheCurrentState(int tok, string& current_state)
{
parcases retval = c_DEFCURRENTSTATE;
if (tok == IDENTIFIER) //IDENTIFIER = a
{
current_state = string(yylval.s); // a,b,c
retval = c_DEFCURRENTSTATE;
}
else if (tok == 'TO') { // State is done
retval = c_DEFNEXTSTATES;
}
return retval;
}
parcases CParser::ScanTheNextStates(int tok, list& next_states)
{
parcases retval = c_DEFNEXTSTATES;
if (tok == IDENTIFIER) //IDENTIFIER = a
{
next_states.push_back(string(yylval.s)); //a, b,x
retval = c_DEFNEXTSTATES;
}
else if (tok == 'OUT') { // States are done
retval = c_DEFOUT;
}
return retval;
}
parcases CParser::ScanTheOutputs(int tok, list& Outputs)
{
parcases retval = c_DEFOUT;
if ((tok == INTEGER1) || (tok == IDENTIFIER)) //IDENTIFIER = 1,0,x
{
Outputs.push_back(string(yylval.s)); //1,0,x
retval = c_DEFOUT;
}
else if (tok == '\n') { // Outputs are done
retval = c_FINISHED;
}
return retval;
}
bool CParser::compare(list& list_one, list& list_two)
{
bool match = false;
if (list_one == list_two)
{
match = true;
//list_two = 0;
}
return match;
}
list CParser::fusion_lines(list& list_one, list& list_two)
{
}
//------------------------------------------------------------------------
/*
* Parse File:
*
*/
void CParser::InitParse(FILE* inp, FILE* err, FILE* lst)
{
/*
* Set up the file state to something useful.
*/
IP_Input = inp;
IP_Error = err;
IP_List = lst;
IP_LineNumber = 1;
ugetflag = 0;
/*
* Define both the enabled token and keyword strings.
*/
IP_init_token_table();
}
//------------------------------------------------------------------------
int CParser::IP_MatchToken(string& tok)
{
int retval;
if (IP_Token_table.find(tok) != IP_Token_table.end()) {
retval = (IP_Token_table[tok]);
}
else {
retval = (0);
}
return retval;
}
//------------------------------------------------------------------------
/*
* yylex:
*
*/
int CParser::yylex()
{
//Locals
int c;
lexstate s;
/*
* Keep on sucking up characters until we find something which
* explicitly forces us out of this function.
*/
for (s = L_START, yytext = ""; 1;) {
c = Getc(IP_Input);
yytext = yytext + (char)c;
if (!ugetflag) {
if (c != EOF)if (prflag)fprintf(IP_List, "%c", c);
}
else ugetflag = 0;
switch (s) {
//Starting state, look for something resembling a token.
case L_START:
if (isdigit(c)) {
s = L_INT;
}
else if (isalpha(c) || c == '\\') {
s = L_IDENT;
}
else if (isspace(c)) {
if (c == '\n') {
IP_LineNumber += 1;
if (prflag)
fprintf(IP_List, "%5d ", (int)IP_LineNumber);
}
yytext = "";
}
else if (c == '/') {
yytext = "";
s = L_COMMENT;
}
else if (c == '"') {
s = L_STRING;
}
else if (c == EOF) {
return ('\0');
}
else {
return (c);
}
break;
case L_COMMENT:
if (c == '/')
s = L_LINE_COMMENT;
else if (c == '*')
s = L_TEXT_COMMENT;
else {
Ungetc(c);
return('/'); /* its the division operator not a comment */
}
break;
case L_LINE_COMMENT:
if (c == '\n') {
s = L_START;
Ungetc(c);
}
yytext = "";
break;
case L_TEXT_COMMENT:
if (c == '\n') {
IP_LineNumber += 1;
}
else if (c == '*')
s = L_END_TEXT_COMMENT;
yytext = "";
break;
case L_END_TEXT_COMMENT:
if (c == '/') {
s = L_START;
}
else {
s = L_TEXT_COMMENT;
}
yytext = "";
break;
/*
* Suck up the integer digits.
*/
case L_INT:
if (isdigit(c)) {
break;
}
else {
Ungetc(c);
yylval.s = yytext.substr(0, yytext.size() - 1);
yylval.i = atoi(yylval.s.c_str());
return (INTEGER1);
}
break;
/*
* Grab an identifier, see if the current context enables
* it with a specific token value.
*/
case L_IDENT:
if (isalpha(c) || isdigit(c) || c == '_')
break;
Ungetc(c);
yytext = yytext.substr(0, yytext.size() - 1);
yylval.s = yytext;
if (c = IP_MatchToken(yytext)) {
return (c);
}
else {
return (IDENTIFIER);
}
/*
* Suck up string characters but once resolved they should
* be deposited in the string bucket because they can be
* arbitrarily long.
*/
case L_STRING2:
s = L_STRING;
if (c == '"') {
PushString((char)c);
}
else {
if (c == '\\') {
PushString((char)c);
}
else {
PushString((char)'\\');
PushString((char)c);
}
}
break;
case L_STRING:
if (c == '\n')
IP_LineNumber += 1;
else if (c == '\r')
;
else if (c == '"' || c == EOF) {
return (STRING1);
}
else if (c == '\\') {
s = L_STRING2;
}
else
PushString((char)c);
break;
default: printf("***Fatal Error*** Wrong case label in yylex\n");
}
}
}
//------------------------------------------------------------------------
int main(int argc, char* argv[])
{
FILE* inf;
CParser obj;
//char fistr[100];
string fistr;
printf("Enter filename:\n");
//gets(fistr);
cin >> fistr;
inf = fopen(fistr.c_str(), "r");
if (inf == NULL) {
printf("Cannot open input file %s\n", fistr);
return 0;
}
CParser obj;
obj.InitParse(inf, stderr, stdout);
// obj.pr_tokentable();
obj.yyparse();
return 0;
//system("pause");
}
I wrote this classes to put the current state, the next states and the output in a vector
parcases CParser::SkipTillBegin(int tok, list& first_lines)
parcases CParser::ScanTheCurrentState(int tok, string& current_state)
parcases CParser::ScanTheNextStates(int tok, list& next_states)
parcases CParser::ScanTheOutputs(int tok, list& Outputs)
this class is the lexical analyzer it differentiates the integers of identifiers and strings
int CParser::yylex()
The other classes aren't really important to understand.
I wrote this class to compare
bool CParser::compare(list& list_one, list& list_two)
{
bool match = false;
if (list_one == list_two)
{
match = true;
//list_two = 0;
}
return match;
}
and I started this one to merge two lines together
list CParser::fusion_lines(list& list_one, list& list_two) but i am stuck here and on the output class int CParser::yyparse()
I couldn't find a way to do it correctly and to compare two different lines of the text file with the lexical analyzer.
I have this piece of code:
#include "stdafx.h"
#include "afx.h"
...
char * connectionType;
...
int readParameters() {
...
//hFile is a file handler previously initialized
result = readParameter(hFile, connectionType);
if (strcmp(connectionType, "3") == 0) {
//do something
} else {
//do other thing
}
...
}
int readParameter(HANDLE hFile, OUT char * buffer) {
BOOL bResult = true;
BOOL continueLine = true;
char inBuffer[1];
DWORD bytesToRead = 1;
DWORD bytesRead = 0;
OVERLAPPED stOverlapped = {0};
char parameter[256] = {};
int counter = 0;
while (continueLine) {
bResult = ReadFile(hFile, inBuffer, sizeof(char), &bytesRead, &stOverlapped);
if (!bResult) {
return 0;
} else if (inBuffer[0] == '\n' || bytesRead == 0) {
continueLine = false;
} else {
parameter[counter] = inBuffer[0];
counter++;
if (bResult && bytesRead == 0) {
continueLinea = false;
}
}
}
parameter[counter] = '\0';
memcpy(buffer, parameter, 256);
return 1;
}
By debugging, I know that the connectionType attribute ends up being a null terminated string "3", but the strcmp method keeps returning 3328 (>0). Is there a problem because "3" is a constant? What might be the problem?
I realized what was the problem with the code. The problem was that connectionType, whose value was a null terminated string "3", was in fact different to the line read from the file, which was actually a "3" plus a carriage return plus a null.
After I added that consideration to the code, my problem was solved.