I try to read the name of a file using scanf but failed.
I am very bad at pointers and could not find the problem.
Is there a problem with the pointer to the array of string?
Here is my code:
int* Read_file(char* str[])
{
FILE* fp = fopen(str[1], "r");
if(fp == NULL)
{
printf("File cannot open\n");
return NULL;
}
int rows = 0;
while(!feof(fp))
{
if(fgetc(fp) == '\n')
{
rows ++;
}
}
rows ++;
int* keys = (int*)malloc(3 * rows * sizeof(int));
fseek(fp, 0L, 0);
while(!feof(fp))
{
for(int i = 0;i < rows;i ++)
{
for(int j = 0;j < 3;j ++)
{
fscanf(fp,"%d", &keys[(3 * i) + j]);
}
}
}
fclose(fp);
return keys;
}
int main()
{
char* str[20];
printf("Build_tree ");
scanf("%s",&str);
int* keys = Read_file(str);
return 0;
}
Okay, so the thing is:
You need a char array to store a string(file-name). So you should use a char array. Instead, you were using an array of char pointers.
An array is actually a series of memory blocks. The name of the array represents a pointer to the first element of the array(in this case the first char variable).
While reading a string, scanf needs a location to store it. So you need to give it the address of the first char variable of your char array, which is available in your char array itself. So you have to pass str only to scanf. In the case of normal int,float, and such fundamental data types, their names represent memory blocks and not pointers to memory blocks, and hence you had to use a &.
Then for fopen, fopen expects a char*(which points to the first character of the char array stoing the filename) and you have to provide it with a char* . So you should pass str.
I think your code should go like
int* Read_file(char str[])
{
FILE* fp = fopen(str, "r");
if(fp == NULL)
{
printf("File cannot open\n");
return NULL;
}
int rows = 0;
while(!feof(fp))
{
if(fgetc(fp) == '\n')
{
rows ++;
}
}
rows ++;
int* keys = (int*)malloc(3 * rows * sizeof(int));
fseek(fp, 0L, 0);
while(!feof(fp))
{
for(int i = 0;i < rows;i ++)
{
for(int j = 0;j < 3;j ++)
{
fscanf(fp,"%d", &keys[(3 * i) + j]);
}
}
}
fclose(fp);
return keys;
}
int main()
{
char str[20];
printf("Build_tree ");
scanf("%s",str);
int* keys = Read_file(str);
//Whatever you want to do with the keys
return 0;
}
Comment for any queries.
Related
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 been stuck in pset5 for a while now. No matter from which angle I look to my code I cannot fin out what is wrong with it. I set my number of buckets randomly to 1000. Can someone find out the problem?
Below is the reply I get from check50
:) dictionary.c, dictionary.h, and Makefile exist
:) speller compiles
:( handles most basic words properly
expected "MISSPELLED WOR...", not "MISSPELLED WOR..."
:) handles min length (1-char) words
:) handles max length (45-char) words
:) handles words with apostrophes properly
:) spell-checking is case-insensitive
:( handles substrings properly
expected "MISSPELLED WOR...", not "MISSPELLED WOR..."
:| program is free of memory errors
can't check until a frown turns upside down
these is what I've done:
const unsigned int N = 1000;
// Returns true if word is in dictionary else false
bool check(const char *word)
{
//convert *word to lowercase so that the hash function is case-insensitive
int length = strlen(word);
char copy[length + 1];
for (int i = 0; i < length; i++)
{
copy[i] = tolower(word[i]);
}
// create a variable to return hashed value of word
int index_check = hash(copy);
//create cursor to traverse the linked list
node *cursor = table[index_check];
//check if word is in the linked list
while (cursor != NULL)
{
if (strcasecmp(word, cursor->word) == 0)
{
return true;
}
cursor = cursor->next;
}
//return false if cursor->next = NULL has been reached
return false;
}
// Hashes word into a number
unsigned int hash(const char *word)
{
// Source of hash function: stackoverflow.com/questions/14409466/simple-hash-functions
unsigned int count;
unsigned int hashValue = 0;
for(count = 0; word[count] != '\0'; count++)
{
hashValue = word[count] + (hashValue << 6) + (hashValue << 16) - hashValue;
}
return (hashValue % N);
}
// Loads dictionary into memory, returning true if successful else false
bool load(const char *dictionary)
{
// open dictionary file
FILE *f = fopen(dictionary, "r");
if (f == NULL)
{
printf("Dictionary could not be opened\n");
return false;
}
//initialize string as a buffer, to be used in next function, fscanf
char buffer[LENGTH + 1];
//loop to check whether end of file has been reached
while (fscanf(f, "%s", buffer) != EOF)
{
//read words from file into buffer
fscanf(f, "%s", buffer);
//allocate memory for a node and check if NULL
node *n = malloc(sizeof(node));
if (n == NULL)
{
printf("Could not allocate memmory (malloc *n)\n");
return false;
}
//copy "buffer" into the node created
strcpy(n->word, buffer);
//call hash function
int index = hash(buffer);
//check if it's the first word being inserted into that bucket
if (table[index] == NULL)
{
table[index] = n;
}
else
{
n->next = table[index];
table[index] = n;
}
size_dictionary++;
}
fclose(f);
return true;
}
// Unloads dictionary from memory, returning true if successful else false
bool unload(void)
{
node *cursor;
node *tmp;
// run thru all buckets
for(int i = 0; i < N; i++)
{
//check if bucket isn't NULL
if(table[i] != NULL)
{
cursor = table[i];
tmp = cursor;
while (tmp != NULL)
{
cursor = cursor->next;
free(tmp);
tmp = cursor;
}
}
}
return true;
}
The clue is in WORDS IN DICTIONARY. Only half the words in dictionary are loaded. That is because of the back-to-back fscanf in load.
tiagoK, I think I'm having the same issue here and been stuck with this exercice for days. What did you mean by " the loop should have been done with lenght + 1", where did you declare this string ? I've tried it on the strcasecmp field and the hash one but it doesn't change anything. Do you remember ?
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.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;
// Choose number of buckets in hash table
const unsigned int N = 17576;
// Hash table
node *table[N];
// Initialize new variables used in the program
unsigned int numberofwords;
unsigned int hashvalue;
// Returns true if word is in dictionary, else false
bool check(const char *word)
{
// Hash the word to determine its hash value
hashvalue = hash(word);
// Set cursor to the head of the linked list of the word
node* cursor = table[hashvalue];
// Traverse into the linked list comparing the word to find a correspondance while the cursor isn't pointing to null
while (cursor != NULL)
{
if (strcasecmp(cursor->word, word) == 0)
{
return true;
}
cursor = cursor->next;
}
return false;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
// Inittialize a variable to keep track of the total of ASCII values
unsigned int sum = 0;
// Loop in all the letters in the word
for (int i = 0 ; i < strlen(word) ; i++)
{
// Ignore digits
if (isdigit(word[i]))
{
continue;
}
else if (isalpha(word[i]) || word[i] == ('\''))
{
// Check the first three character in the word while setting them lower letters in order to get they ASCII value
sum += tolower(word[0] - 'a');
sum += tolower(word[1] - 'a');
sum += tolower(word[2] - 'a');
}
}
// Divide the total of the three letters by the number of buckets
hashvalue = sum % N;
return hashvalue;
}
// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
// Open the file
FILE* file = fopen(dictionary, "r");
char word[LENGTH + 1];
{
if (file != NULL)
{
// While dictionary doesn't return EOF, read strings from the file, one at the time
while (fscanf(file, "%s", word) != EOF)
{
// Create a new node for the word and copy it in the node
node *n = malloc(sizeof(node));
if (n == NULL)
{
return 1;
}
strcpy (n->word, "%s");
// Hash word to obtain a hash value
hashvalue = hash(word);
// Insert word into hash table depending if it's the first word or not
if (table[hashvalue] != NULL)
{
n->next = table[hashvalue];
}
else
{
n->next = NULL;
}
table[hashvalue] = n;
// Add one to the counter
numberofwords++;
}
fclose(file);
}
else
{
fclose(file);
perror("Loading error");
return 1;
}
}
// Return true
return true;
}
// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
// Return the number of words we're keeping track of while loading the dictionary
return numberofwords;
}
// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
// Check all the head of the hash table
for(int i = 0; i < N; i++)
{
// Create a temporary node to not lose the rest of the linked list
node* cursor = table[i];
// Set the cursor to the next node while the temporary value remains at the inital location, then free it before to move to cursor
while(cursor != NULL)
{
node* tmp = cursor;
cursor = cursor->next;
free(tmp);
}
}
return true;
}
I have been running the solution but is getting a high number of misspelled words.
WORDS MISSPELLED: 15904 as compared to staff's WORDS MISSPELLED: 955
Other than that, the word count is accurate and the runtime is alright.
I suspect the problem might come from the check / load function but I am not sure what caused it.
Some other code implemented a "to lowercase" in the check function, but I thought the (strcasecmp) would have done the job of comparing between strings, disregarding the case.
Check Function
bool check(const char *word)
{
int hashInt = hash(word);
if (table[hashInt] == NULL)
{
return 1;
}
node *cursor = table[hashInt];
while (cursor != NULL)
{
int i = strcasecmp(cursor -> word, word);
if (i == 0)
{
return 0;
break;
}
cursor = cursor -> next;
}
return false;
}
Load Function
bool load(const char *dictionary)
{
FILE *file = fopen(dictionary, "r");
if (file == NULL)
{
printf("error opening file");
return 1;
}
char word [LENGTH + 1];
while (fscanf(file, "%s\n", word) != EOF)
{
int hashInt = hash(word);
node *n = malloc(sizeof(node));
if (n == NULL)
{
unload();
return 1;
}
if (table[hashInt] == NULL)
{
table[hashInt] = n;
}
else
{
n -> next = table[hashInt];
table[hashInt] = n;
}
strcpy(n -> word, word);
wordLoaded++;
}
fclose(file);
return true;
}
Hash Function
unsigned int hash(const char *word)
{
unsigned int hash = 0;
for (int i = 0, n = strlen(word); i < n; i++)
hash = (hash << 2) ^ word[i];
return hash % N;
return 0;
}
From the spec:
dictionary is assumed to be a file containing a list of lowercase
words
and
Your implementation of check must be case-insensitive.
This int i = strcasecmp(cursor -> word, word); looks like it would fulfill the requirement as long as this int hashInt = hash(word); was also case insensitive. Alas, it is not; hash("A") and hash("a") will return different values.
We are given a pattern string: 'foo' and a source string: 'foobaroofzaqofom' and we need to find all occurrences of word pattern string in any order of letters. So for a given example solution will looks like: ['foo', 'oof', 'ofo'].
I have a solution, but i'm not sure that it is the most efficient one:
Create hash_map of chars of pattern string where each char is a key and each value is a counter of chars in pattern. For a given example it would be {{f: 1}, {o: 2}}
Look through the source string and if found one of the elements from hash_map, than try to find all the rest elements of pattern
If all elements are found than it is our solution, if not going forward
Here is an implementation in c++:
set<string> FindSubstringPermutations(string& s, string& p)
{
set<string> result;
unordered_map<char, int> um;
for (auto ch : p)
{
auto it = um.find(ch);
if (it == um.end())
um.insert({ ch, 1 });
else
um[ch] += 1;
}
for (int i = 0; i < (s.size() - p.size() + 1); ++i)
{
auto it = um.find(s[i]);
if (it != um.end())
{
decltype (um) um_c = um;
um_c[s[i]] -= 1;
for (int t = (i + 1); t < i + p.size(); ++t)
{
auto it = um_c.find(s[t]);
if (it == um_c.end())
break;
else if (it->second == 0)
break;
else
it->second -= 1;
}
int sum = 0;
for (auto c : um_c)
sum += c.second;
if (sum == 0)
result.insert(s.substr(i, p.size()));
}
}
return result;
}
Complexity is near O(n), i don't know how to calculate more precisely.
So the question: is there any efficient solution, because using hash_map is a bit of hacks and i think there may be more efficient solution using simple arrays and flags of found elements.
You could use a order-invariant hash-algorithm that works with a sliding window to optimize things a bit.
An example for such a hash-algorithm could be
int hash(string s){
int result = 0;
for(int i = 0; i < s.length(); i++)
result += s[i];
return result;
}
This algorithm is a bit over-simplistic and is rather horrible in all points except performance (i.e. distribution and number of possible hash-values), but that isn't too hard to change.
The advantage with such a hash-algorithm would be:
hash("abc") == hash("acb") == hash("bac") == ...
and using a sliding-window with this algorithm is pretty simple:
string s = "abcd";
hash(s.substring(0, 3)) + 'd' - 'a' == hash(s.substring(1, 3));
These two properties of such hashing approaches allow us to do something like this:
int hash(string s){
return sum(s.chars);
}
int slideHash(int oldHash, char slideOut, char slideIn){
return oldHash - slideOut + slideIn;
}
int findPermuted(string s, string pattern){
int patternHash = hash(pattern);
int slidingHash = hash(s.substring(0, pattern.length()));
if(patternHash == slidingHash && isPermutation(pattern, s.substring(0, pattern.length())
return 0;
for(int i = 0; i < s.length() - pattern.length(); i++){
slidingHash = slideHash(slidingHash, s[i], s[i + pattern.length()]);
if(patternHash == slidingHash)
if(isPermutation(pattern, s.substring(i + 1, pattern.length())
return i + 1;
}
return -1;
}
This is basically an altered version of the Rabin-Karp-algorithm that works for permuted strings. The main-advantage of this approach is that less strings actually have to be compared, which brings quite a bit of an advantage. This especially applies here, since the comparison (checking if a string is a permutation of another string) is quite expensive itself already.
NOTE:
The above code is only supposed as a demonstration of an idea. It's aimed at being easy to understand rather than performance and shouldn't be directly used.
EDIT:
The above "implementation" of an order-invariant rolling hash algorithm shouldn't be used, since it performs extremely poor in terms of data-distribution. Of course there are obviously a few problems with this kind of hash: the only thing from which the hash can be generated is the actual value of the characters (no indices!), which need to be accumulated using a reversible operation.
A better approach would be to map each character to a prime (don't use 2!!!). Since all operations are modulo 2^(8 * sizeof(hashtype)) (integer overflow), we need to generate a table of the multiplicative inverses modulo 2^(8 * sizeof(hashtype)) for all used primes. I won't cover generating these tables, as there's plenty of resources available on that topic here already.
The final hash would then look like this:
map<char, int> primes = generatePrimTable();
map<int, int> inverse = generateMultiplicativeInverses(primes);
unsigned int hash(string s){
unsigned int hash = 1;
for(int i = 0; i < s.length(); i++)
hash *= primes[s[i]];
return hash;
}
unsigned int slideHash(unsigned int oldHash, char slideOut, char slideIn){
return oldHash * inverse[primes[slideOut]] * primes[slideIn];
}
Keep in mind that this solution works with unsigned integers.
Typical rolling hashfunction for anagrams
using product of primes
This will only work for relatively short patterns
The hashvalues for allmost all normal words will fit into a 64 bit value without overflow.
Based on this anagram matcher
/* braek; */
/* 'foobaroofzaqofom' */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef unsigned long long HashVal;
static HashVal hashchar (unsigned char ch);
static HashVal hashmem (void *ptr, size_t len);
unsigned char primes26[] =
{ 5,71,79,19,2,83,31,43,11,53,37,23,41,3,13,73,101,17,29,7,59,47,61,97,89,67, };
/*********************************************/
static HashVal hashchar (unsigned char ch)
{
HashVal val=1;
if (ch >= 'A' && ch <= 'Z' ) val = primes26[ ch - 'A'];
else if (ch >= 'a' && ch <= 'z' ) val = primes26[ ch - 'a'];
return val;
}
static HashVal hashmem (void *ptr, size_t len)
{
size_t idx;
unsigned char *str = ptr;
HashVal val=1;
if (!len) return 0;
for (idx = 0; idx < len; idx++) {
val *= hashchar ( str[idx] );
}
return val;
}
/*********************************************/
unsigned char buff [4096];
int main (int argc, char **argv)
{
size_t patlen,len,pos,rotor;
int ch;
HashVal patval;
HashVal rothash=1;
patlen = strlen(argv[1]);
patval = hashmem( argv[1], patlen);
// fprintf(stderr, "Pat=%s, len=%zu, Hash=%llx\n", argv[1], patlen, patval);
for (rotor=pos=len =0; ; len++) {
ch=getc(stdin);
if (ch == EOF) break;
if (ch < 'A' || ch > 'z') { pos = 0; rothash = 1; continue; }
if (ch > 'Z' && ch < 'a') { pos = 0; rothash = 1; continue; }
/* remove old char from rolling hash */
if (pos >= patlen) { rothash /= hashchar(buff[rotor]); }
/* add new char to rolling hash */
buff[rotor] = ch;
rothash *= hashchar(buff[rotor]);
// fprintf(stderr, "%zu: [rot=%zu]pos=%zu, Hash=%llx\n", len, rotor, pos, rothash);
rotor = (rotor+1) % patlen;
/* matched enough characters ? */
if (++pos < patlen) continue;
/* correct hash value ? */
if (rothash != patval) continue;
fprintf(stdout, "Pos=%zu\n", len);
}
return 0;
}
Output/result:
$ ./a.out foo < anascan.c
Pos=21
Pos=27
Pos=33
Update. For people who don't like product of primes, here is a taxinumber sum of cubes (+ additional histogram check) implementation. This is also supposed to be 8-bit clean. Note the cubes are not necessary; it wotks equally well with squares. Or just the sum. (the final histogram check will have some more work todo)
/* braek; */
/* 'foobaroofzaqofom' */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef unsigned long long HashVal;
static HashVal hashchar (unsigned char ch);
static HashVal hashmem (void *ptr, size_t len);
/*********************************************/
static HashVal hashchar (unsigned char ch)
{
HashVal val=1+ch;
return val*val*val;
}
static HashVal hashmem (void *ptr, size_t len)
{
size_t idx;
unsigned char *str = ptr;
HashVal val=1;
if (!len) return 0;
for (idx = 0; idx < len; idx++) {
val += hashchar ( str[idx] );
}
return val;
}
/*********************************************/
int main (int argc, char **argv)
{
size_t patlen,len,rotor;
int ch;
HashVal patval;
HashVal rothash=1;
unsigned char *patstr;
unsigned pathist[256] = {0};
unsigned rothist[256] = {0};
unsigned char cycbuff[1024];
patstr = (unsigned char*) argv[1];
patlen = strlen((const char*) patstr);
patval = hashmem( patstr, patlen);
for(rotor=0; rotor < patlen; rotor++) {
pathist [ patstr[rotor] ] += 1;
}
fprintf(stderr, "Pat=%s, len=%zu, Hash=%llx\n", argv[1], patlen, patval);
for (rotor=len =0; ; len++) {
ch=getc(stdin);
if (ch == EOF) break;
/* remove old char from rolling hash */
if (len >= patlen) {
rothash -= hashchar(cycbuff[rotor]);
rothist [ cycbuff[rotor] ] -= 1;
}
/* add new char to rolling hash */
cycbuff[rotor] = ch;
rothash += hashchar(cycbuff[rotor]);
rothist [ cycbuff[rotor] ] += 1;
// fprintf(stderr, "%zu: [rot=%zu], Hash=%llx\n", len, rotor, rothash);
rotor = (rotor+1) % patlen;
/* matched enough characters ? */
if (len < patlen) continue;
/* correct hash value ? */
if (rothash != patval) continue;
/* correct histogram? */
if (memcmp(rothist,pathist, sizeof pathist)) continue;
fprintf(stdout, "Pos=%zu\n", len-patlen);
}
return 0;
}
Here is the entire program, please help me, I've tried everything to find out what exactly is going with the memory. The problem is everything runs perfectly, but there are some extra characters printed with output.
Here is the .h file:
class MyString
{
public:
MyString();
MyString(const char *message);
MyString(const MyString &source);
~MyString();
const void Print() const;
const int Length() const;
MyString& operator()(const int index, const char b);
char& operator()(const int i);
MyString& operator=(const MyString& rhs);
bool operator==(const MyString& other) const;
bool operator!=(const MyString& other) const;
const MyString operator+(const MyString& rhs) const;
MyString& operator+=(const MyString& rhs);
friend ostream& operator<<(ostream& output, const MyString& rhs);
const int Find(const MyString& other);
MyString Substring(int start, int length);
private:
char *String;
int Size;
};
istream& operator>>(istream& input, MyString& rhs);
The .cpp file:
MyString::MyString()
{
char temp[] = "Hello World";
int counter(0);
while(temp[counter] != '\0')
{
counter++;
}
Size = counter;
String = new char [Size];
for(int i=0; i < Size; i++)
String[i] = temp[i];
}
//alternate constructor that allows for setting of the inital value of the string
MyString::MyString(const char *message)
{
int counter(0);
while(message[counter] != '\0')
{
counter++;
}
Size = counter;
String = new char [Size];
for(int i=0; i < Size; i++)
String[i] = message[i];
}
//copy constructor
MyString::MyString(const MyString &source)
{
int counter(0);
while(source.String[counter] != '\0')
{
counter++;
}
Size = counter+1;
String = new char[Size];
for(int i = 0; i <= Size; i++)
String[i] = source.String[i];
}
//Deconstructor
MyString::~MyString()
{
delete [] String;
}
//Length() method that reports the length of the string
const int MyString::Length() const
{
int counter(0);
while(String[counter] != '\0')
{
counter ++;
}
return (counter);
}
/*Parenthesis operator should be overloaded to replace the Set and Get functions of your previous assignment. Note that both instances should issue exit(1) upon violation of the string array bounaries.
*/
MyString& MyString::operator()(const int index, const char b)
{
if(String[index] == '\0')
{
exit(1);
}
else
{
String[index] = b;
}
}
char& MyString::operator()(const int i)
{
if(String[i] == '\0')
{
exit(1);
}
else
{
return String[i];
}
}
/*Assignment operator (=) which will copy the source string into the destination string. Note that size of the destination needs to be adjusted to be the same as the source.
*/
MyString& MyString::operator=(const MyString& rhs)
{
if(this != &rhs)
{
delete [] String;
String = new char[rhs.Size];
Size = rhs.Size;
for(int i = 0; i < rhs.Size+1 ; i++)
{
String[i] = rhs.String[i];
}
}
return *this;
}
/*Logical comparison operator (==) that returns true iff the two strings are identical in size and contents.
*/
bool MyString::operator==(const MyString& other)const
{
if(other.Size == this->Size) {
for(int i = 0; i < this->Size+1; i++)
{
if(&other == this)
return true;
}
}
else
return false;
}
//Negated logical comparison operator (!=) that returns boolean negation of 2
bool MyString::operator!=(const MyString& other) const
{
return !(*this == other);
}
//Addition operator (+) that concatenates two strings
const MyString MyString::operator+(const MyString& rhs) const
{
char* tmp = new char[Size + rhs.Size +1];
for(int i = 0; i < Size; i++)
{
tmp[i] = String[i];
}
for(int i = 0; i < rhs.Size+1; i++) {
tmp[i+Size] = rhs.String[i];
}
MyString result;
delete [] result.String;
result.String = tmp;
result.Size = Size+rhs.Size;
return result;
}
/*Addition/Assigment operator (+=) used in the following fashion: String1 += String2 to operate as String1 = String1 + String2
*/
MyString& MyString::operator+=(const MyString& rhs)
{
char* tmp = new char[Size + rhs.Size + 1];
for(int i = 0; i < Size; i++) {
tmp[i] = String[i];
}
for(int i = 0; i < rhs.Size+1; i++)
{
tmp[i+Size] = rhs.String[i];
}
delete [] String;
String = tmp;
Size += rhs.Size;
return *this;
}
istream& operator>>(istream& input, MyString& rhs)
{
char* t;
int size(256);
t = new char[size];
input.getline(t,size);
rhs = MyString(t);
delete [] t;
return input;
}
ostream& operator<<(ostream& output, const MyString& rhs)
{
if(rhs.String != '\0')
{
output << rhs.String;
}
else
{
output<<"No String to output\n";
}
return output;
}
/*MyString::Find that finds a string in a larger string and returns the starting location of the substring. Note that your string location starts from 0 and ends at length -1. If the string is not found, a value of -1 will be returned
*/
const int MyString::Find(const MyString& other)
{
int nfound = -1;
if(other.Size > Size)
{
return nfound;
}
int i = 0, j = 0;
for(i = 0; i < Size; i++) {
for(j = 0; j < other.Size; j++) {
if( ((i+j) >= Size) || (String[i+j] != other.String[j]) )
{
break;
}
}
if(j == other.Size)
{
return i;
}
}
return nfound;
}
/*MyString::Substring(start, length). This method returns a substring of the original string that contains the same characters as the original string starting at location start and is as long as length.
*/
MyString MyString::Substring(int start, int length)
{
char* leo = new char[length+1];
for(int i = start; i < start + length+1; ++i)
{
leo[i-start] = String[i];
}
MyString sub;
delete [] sub.String; sub.String = leo; sub.Size = Size;
return sub;
}
//Print() method that prints the string
const void MyString::Print() const
{
for(int i=0; i < Size; i++)
{
cout<<String[i];
}
cout<<endl;
}
The main.cpp file:
int main (int argc, char **argv)
{
MyString String1;
const MyString ConstString("Target string"); //Test of alternate constructor
MyString SearchString; //Test of default constructor that should set "Hello World".
MyString TargetString (String1); //Test of copy constructor
cout << "Please enter two strings. ";
cout << "Each string needs to be shorter than 256 characters or terminated by /\n." << endl;
cout << "The first string will be searched to see whether it contains exactly the second string. " << endl;
cin >> SearchString >> TargetString; // Test of cascaded string-extraction operator
if(SearchString.Find(TargetString) == -1) {
cout << TargetString << " is not in " << SearchString << endl;
}
else {
cout << TargetString << " is in " << SearchString << endl;
cout << "Details of the hit: " << endl;
cout << "Starting position of the hit: " << SearchString.Find(TargetString) << endl;
cout << "The matching substring is: " << SearchString.Substring(SearchString.Find(TargetString), TargetString.Length()-1)<<"\n";
}
return 0;
}
Running the program you get this:
Please enter two strings. Each string needs to be shorter than 256 characters or terminated by /
.
The first string will be searched to see whether it contains exactly the second string.
firstly
real
realt World is not in firstly
Please Help!!
try adding a '\0' at the end of your strings in your MyString::MyString(const char *message) constructor
#Sam's answer is correct. I'm going to add on to it to help you learn what's happening.
C and C++ strings are really character arrays that follow a convention that the string is terminated with \0, sometimes called NUL (not null), which is a character where all bits are 0.
Your code gets the first part right in that it creates an array of characters. However, you do not apply the convention that the string must be NUL terminated.
You then pass a string that does not follow the NUL termination convention to cout, which does follow that convention. In other words, it runs through the string, printing each character to stdout, until it happens across the character \0 in memory. It's actually fairly lucky that it terminates. If there were not a \0 in the character array it is outputing, it would just keep on going until reaching a memory address that does not belong to your program and failing with a segmentation fault.