Garbage values are printing even after null character - c++

This code is for printing left recursion alternatives in a production.But when printing alternatives at last garbage values are being added.Why am i getting such error?
how to resolve such problem?in the image the output is displayed.but that is the wrong output
#include<iostream>
#include<cstring>
using namespace std;
class production
{
private:
char lhs;
char rhs[10][10],lr[10][10];
int noa;
public:
production()
{
noa=0;
}
void makeprod(char *str)
{
lhs=str[0];
char r[20];
strcpy(r,str+3);
int j=0;
for(int i=0;r[i]!='\0';i++)
{
if(r[i]!='/')
rhs[noa][j++]=r[i];
else
{
rhs[noa++][j]='\0';
j=0;
}
}
noa++;
}
void checkLR()
{
int ct=0,m=0;
for(int i=0;i<noa;i++)
if(lhs==rhs[i][0])
{
strcpy(lr[m],rhs[i]);
m++;
ct++;
}
if(ct>0)
{
for(int k=0;k<ct;k++)
cout<<"Left recursion at "<<lr[k]<<"\n";
}
else
cout<<"no\n";
}
void printprod()
{
cout<<"LHS = "<<lhs<<"\n";
cout<<"RHS = ";
for(int i=0;i<noa;i++)
cout<<rhs[i]<<" ";
}
};
int main()
{
production p;
char str[20];
cout<<"enter a production\n";
cin>>str;
p.makeprod(str);
p.printprod();
p.checkLR();
return 0;
}

In makeprod you're checking your input string for / to add the termination character to your rhs-array, and thus your input string has to end with the / sign. You have several choices :
either initialize your arrays with 0, so the rhs-array is always null-terminated (independently from your question : It's always good practice to initialize your variables)
add a null-termination sign (0) to rhs when the end of your input string is reached
edit: Just place a memset(rhs, 0, sizeof(rhs)*sizeof(char)); (the same for lr) in your constructor and the output should be fine. This will initialize your arrays with zeroes and thus the string is null-terminated.
But you really should add some overflow checking.

You are propably incrementing noa one time too much in makeprod().
That makes the for loop in printprod() access rhs 1 element after its end.

Related

Character counter returning incorrect value for char[n] array

I am writing a C++ program for homework, and it needs to count the characters in a char arr[n] string. However, my counter keeps returning the wrong values. I have looked through other answers to similar questions, however, they are not specific to C++ and none of the answers explain the value I am getting.
#include<iostream>
using namespace std;
#include<string.h>
#include <stdlib.h>
class Counter
{
public:
char word[20];
int totChar{ 0 };
void setWord(char word)
{
this->word[20] = word;
}
void setCount(int totChar)
{
this->totChar = totChar;
}
int getLength()
{
return totChar;
}
void charCount()
{
int n = 0;
for (int i = 0; word[i] != '\0'; i++) {
if (word[i] != '\0')
{
n++;
}
}
setCount(n);
}
};
int main()
{
char text[20];
cout << "Enter the string:" << endl;
cin >> text;
Logic input;
input.setWord(text[20]);
input.charCount();
// input.resetWord();
cout << input.getLength();
}
So it seems you haven't figured out how arrays and C strings work in C++ yet.
void setWord(const char* word)
{
strcpy(this->word, word);
}
and
Logic input;
input.setWord(text);
Your code is a bit weird, I guess you are just experimenting, but I think those two changes should make it work.

Debug my reimplementation of strtok and split

I'm supposed to be writing code that takes a string of comma separated values without spaces (ex. my,name,is,jack). First we had to write a function
string nextstring(string str, int start_index)
that returns a single "value" from your initial string depending on the start index. The second part of the problem was to write a function
int split(string str, string a[], int max_size)
that will identify all the values in the initial string and put them in a string array and then return the total number of values stored in the array; i.e. if you had initially input my,name,is it would return 3.
My function never returns the correct value and whatever it returns changes depending on what the length of the words are.
#include <iostream>
#include <string>
using namespace std;
string nextstring(string str, int start_index);
int split(string str, string a[], int max_size);
int main()
{
string str;
int cnt;
string a[100];
cout<< "what is your string" << endl;
getline(cin, str);
cnt= split(str, a, 100);
cout << "There are " << cnt << " values in this string" << endl;
for(int i=0; i<cnt; i++)
{
cout << a[i] << endl;
}
return 0;
}
string nextstring(string str, int start_index)
{
string ans;
if(str[start_index] == ',' || str[start_index] == '\0')
{
ans=" ";
}
else{
ans=str[start_index]+nextstring(str, start_index+1);
}
return ans;
}
int split(string str, string a[], int max_size)
{
int j=0;
int ans=0;
double k=0;
while(j<max_size)
{
a[j]= nextstring(str,k);
string check=a[j];
if(isalpha(check[0])!= 0)
{
ans++;
}
k=k+a[j].length();
j++;
}
return ans;
}
It seems that your problem is that while(j<max_size){...} leads to j being incremented up to max_size. The line a[j]= nextstring(str,k); is at some points reading values that are outside your string which is really bad!
Replacing while(j<max_size){...} by while(j<max_size && k<str.length()){...} seems to be enough to make your code work!
Apart from that:
k has no reason to be a double! It should be an int (or something similar).
Since you are already using string, you should also learn to use vector. split is better written as:
int split(string str, vector<string> &a, int max_size)
{
int ans=0;
int k=0;
while(k<str.length())
{
string next = nextstring(str,k);
if(isalpha(next[0])!= 0)
{
ans++;
a.append(next);
}
k += next.length();
}
return ans;
}
The problem in your approach is to identify the end of the string, as there is no null terminator in a c++ string. Consider to update nextstring() to look for the end of string in a different manner:
string nextstring(string str, int start_index)
{
...
if(start_index == str.size() || str[start_index] == ',' ) //<===
{
ans=" ";
}
...
}
online demo
Additional recommendation
Note that it is not very nice to return a blank string when in reality it should be empty to reflect its real value (e.g. ",,"). You have no choice because otherwise you would have no mean in the calling function, to determine that the end of string was reached. But the consequence is thar all your strings have a trailing blank.
When you call recursively the function adding char to build the return string, you risk to have a considerable overhead. You could consider avoiding this, by replacing the else part:
ans=str.substr(start_index, str.find(',', start_index+1)-start_index);
However, as you have no trailing blank anymore, you need to adapt split() so to adapt its way to count the total number of chars parsed:
k=k+a[j].length()+1; // +1 because there's no longer a trailing blank.
Online demo

what is the issue with the below code?

The question is to replace the spaces contained in a string with a "%20". So basically need to insert this in a string wherever there is a space. Therefore, I want to replace all spaces with %20 but only partial string is getting replaced. I can see the correct o/p in the replace function
#include<iostream>
#include<string>
using namespace std;
int spaces(char* s,int size) /*calculate number of spaces*/
{
int nspace=0;
for(int i=0;i<size;i++)
{
if(s[i]==' ')
{
nspace++;
}
}
return nspace;
}
int len_new_string(char* inp,int l) /*calculate the length of the new string*/
{
int new_length=l+spaces(inp,l)*2;
return new_length;
}
char* replace(char* s,int length) /*function to replace the spaces within a string*/
{
int len=len_new_string(s,length);
char new_string[len];
int j=0;
for(int i=0;i<length;i++)
{
if(s[i]==' ') /*code to insert %20 if space is found*/
{
new_string[j]='%';
new_string[j+1]='2';
new_string[j+2]='0';
j=j+3;
}
else /*copy the original string if no space*/
{
new_string[j]=s[i];
j++;
}
}
cout<<"Replaced String: "<<new_string<<endl;
return s=new_string;
}
int main()
{
char str[]="abc def ghi ";
int length=sizeof(str)/sizeof(str[0]);
cout<<"String is: "<<str<<endl;
char *new_str=replace(str,length);
cout<<"Replaced String is: "<<new_str<<endl;
}
The char array should go out of scope and be released. The only reason you don't get a segfault is that apparently no other program has reserved the memory in that spot yet. To avoid this, try using a char array with padding, handing it over by reference or pointer and filling it in place:
void replace(char *in, char *out, size_t length)
{
/* copy as-is for non-spaces, insert replacement for spaces */
}
int main()
{
char str[]="abc def ghi";
size_t buflen(strlen(str)+2*spaces(str, strlen(str)));
char output[buflen+1];
memset(output, 0, buflen+1);
replace(str, output, strlen(str));
}
Another option is to new[] the return array (remember to delete[] it afterwards, then!) or, which I think you left out for a reason, use std::string all along to avoid the array issue.

Delete repeated characters from a random word

I'm making a class to delete repeated character from a random word. For example if the input is "aabbccddeeff", it should output "abcdef". However my output contains strange characters after "abcdef". The main.cpp file already exists as the requirements for creating the class. Please see the following codes:
main.ccp
#include <iostream>
#include "repeatdeletion.h"
using namespace std;
int main()
{
char* noRepeats;
int length;
string s;
cout<<"Enter a random word with repeating characters: ";
cin>>s;
RepeatDeletion d;
length=s.length();
noRepeats=d.deleteRepeats(s, length);
cout<<"Your word without any repeating characters: ";
for (int k=0; k<length; k++){
cout<<noRepeats[k];
}
cout<<endl;
delete [] noRepeats;
noRepeats=NULL;
return 0;
}
repeatdeletion.h
#ifndef REPEATDELETION_H
#define REPEATDELETION_H
#include <iostream>
using namespace std;
class RepeatDeletion
{
char* c;
char arr[128]={};
bool repeated;
bool isRepeated(char);
public:
RepeatDeletion();
~RepeatDeletion();
char* deleteRepeats(string, int);
};
#endif // REPEATDELETION_H
repeatdeletion.cpp
#include "repeatdeletion.h"
RepeatDeletion::RepeatDeletion()
{
repeated=false;
}
RepeatDeletion::~RepeatDeletion()
{
delete [] c;
c=NULL;
}
bool RepeatDeletion::isRepeated(char c){
bool repeated=false;
if (arr[c]>=1){
repeated=true;
arr[c]++;
}else{
arr[c]++;
}
return repeated;
}
char* RepeatDeletion::deleteRepeats(string str, int len){
c=new char[len];
int j=0;
for (int i=0; i<len; i++){
if (isRepeated(str[i])==false){
c[j]=str[i];
j++;
}
}
return c;
}
Your return character array is not null terminated.
The length function of string does not include \0.
You have two choices
Add null at the end of returned character array, and std::cout the char array directly (instead of char by char)
Output the final length of your char array, and use that as range to print it char by char
Your printing loop loops using the old and unmodified string length. That means you will go outside the characters you added to memory returned by deleteRepeats.
The easiest solution to handle this is to terminate the data as a proper string, and check for the terminator in the loop.
If you want to use a C-string array, they have a null terminator at the end. That means you'll want to (in deleteRepeats) define your character array one character larger than the length:
c=new char[len+1];
And, after the for loop, ensure you put that null terminator in:
c[j] = '\0';
Then, in your calling function, you can just do:
cout << noRepeats;
Even if you don't want to use C strings, you'll need to communicate the new length back to the caller somehow (currently, you're using the original length). The easiest way to do that is (IMNSHO) still using a C-style string and using strlen to get the new length (a).
Otherwise, you're going to need something like a reference parameter for the new length, populated by the function and used by the caller.
(a) But I'd suggest rethinking the way you do things. If you want to be a C++ coder, be a C++ coder. In other words, use std::string for strings since it avoids the vast majority of problems people seem to have with C strings.
That's because in your code you write the following:
cout<<"Your word without any repeating characters: ";
for (int k=0; k<length; k++){
cout<<noRepeats[k];
}
cout<<endl;
Here, length refers to the length of the original string (which you, by the way shouldn't pass to your deleteRepeats method). I would suggest you make deleteRepeats return a string and write something like this:
std::string noRepeats = d.deleteRepeats(s);
std::cout << "Your word without any repeating characters: ";
std::cout << noRepeats << std::endl;
C-style string (char *, if you insist) follow the convention that the last character is '\0', indicating that the string ends. You could also change deleteRepeats by appending '\0', i.e.
char* RepeatDeletion::deleteRepeats(string str){
c = new char[str.size() + 1];
int j = 0;
for (int i = 0; i < str.size(); i++){
if(isRepeated(str[i]) == false){
c[j] = str[i];
j++;
}
}
c[j] = '\0';
return c;
}
and in your main
std::cout << noRepeats << std::endl;
instead of the for loop. But really, you should use std::string, and if possible not mix it with char *. Hope that helps.
for(k=0;k<length;k++)
Here length should be the exact length of noRepeats, but not of s
so :
char* RepeatDeletion::deleteRepeats(string str, int len)
should return the length-after too
use std::unique it does what you want:
std::string s{};
std::cin>>s;
auto it = std::unique(std::begin(s), std::end(s));
s.resize(std::distance(std::begin(s),it));
std::cout << s;
the way it works is to go through the range begin to end and move all the remaining elements forward if the current element is equal to the next. It returns the position of the end of the new string (it in this example) but does not actually shorten the string so on the next line we shorten the string to the length equal to the distance of begin() to it.
see live at http://ideone.com/0CeaHW

reversing the letters of the words in a string

I want to reverse the letters of the words in a string and have to store it in the same array.example: i/p: hi how are you o/p: ih woh era uoy. i wrote this programs but it just prints the same string without reversing and the program is not terminating it continues to print something. i cant findout the mistake. please help me and tell me the correct code.
#include<iostream.h>
#include<conio.h>
#include<stdio.h>
void stre(char (&a1)[20], int j1, int i1)
{
char b[20];
for(int k=i1-j1;k<i1;k++)
b[k]=a1[i1-k-1];
for(k=i1-j1;k<i1;k++);
a1[k]=b[k];
}
void main()
{
clrscr();
int j;
char a[20];
gets(a);
for(int i=0;a[i]!='\0';i++)
{
j++;
if(a[i]==' ')
{
stre(a,j,i);
j=0;
}
}
stre(a,j,i);
for(j=0;j<i;j++)
cout<<a[j];
getch();
}
friends after your answers i removed the semicolon in the for loop and also initialized j=0 but still i am not able to get the required output now for the i/p:hi how are you o/p:ihh hi hhi hhi. still need your help.
for(k=i1-j1;k<i1;k++);
a1[k]=b[k];
The semilcolon after the for loop prevents the last action from occuring multiple times.
try
#include <iostream>
using namespace std;
int main() {
int j =0;
char a[20] = "hi how are you";
char b[20] = "";
int l=0;
for(int i=0;a[i]!='\0';i++){
if(a[i]==' ' ||a[i+1]=='\0'){
cout<<j<<' '<<i;
if(a[i+1]=='\0'){
b[l++] = ' ';
}
for(int k=i;k>=j;k--){
b[l]=a[k];
l++;
}
for(int k=j;k<=i;k++){
cout<<b[k];
a[k] = b[k];
}
cout<<endl;
j = i+1;
}
}
cout << a;
return 0;
}
This for loop is terminated by the semi-colon:
for(k=i1-j1;k<i1;k++);
^^^
Here's a version that is not perfect by any means, but at least, it tries to be more like C++ than C:
http://ideone.com/f5vciW
first: tokenize into words and space sequences
//the spaces should be preserved
std::string test("hi how are you"),reference("ih woh era uoy");
std::vector<std::string> tokens;
tokenize(test,tokens);
then reverse the tokens
for (auto& token : tokens)
std::reverse(token.begin(),token.end());
assemble tokens into a string buffer
std::stringstream buf;
for (auto token : tokens)
buf<<token;
check the result
std::string res=buf.str();
assert(res==reference);
where the tokenizer looks like that:
template <typename TContainer,typename TString>
void tokenize(TString input,TContainer& res)
{
if (input.length()<2) {
res.push_back(input);
return;
}
typename TString::const_iterator pos=input.begin();
bool space_state=std::isspace(input[0],std::locale());
for (typename TString::const_iterator it=input.begin(); it!=input.end();
++it) {
bool is_space=std::isspace(*it,std::locale());
if (is_space!=space_state) {
res.push_back(TString(pos,it));
pos=it;
space_state=is_space;
}
}
//the rest
if (pos!=input.end()) {
res.push_back(
TString(
pos,
static_cast<typename TString::const_iterator>(input.end())
));
}
}
you passed j without assigning the value. so it will be garbage.
I can see two things in your code that may not result in the expected output:
1)
int j;
should be replaced with
int j=0;
And
for(k=i1-j1;k<i1;k++);
a1[k]=b[k];
the semicolon after the for loop needs to be removed.
FYI, this mixing of C and C++ code is not recommended (it's compromising readability). please stick to either one of them.
A similar example is shown on this site. They've also used almost the same approach that you'v e chosen(reversing each word by counting number of spaces) by with the help of stack data structure.
Make sure you have the STL library installed in your environment to run this code. Better run this code on linux platform.
Here's another, shorter version, doing the inversion in-line without using an extra buffer:
http://ideone.com/hs9NZ7
the criterion for the tokenizer is the change in the isspace condition:
auto next_token=
[&](char c) {
return std::isspace(c,loc)!=std::isspace(*pos,loc);
};
using that we can go through the input string and visit the tokens:
for (auto it=std::find_if(pos,test.end(),next_token);
it!=test.end();
it=std::find_if(pos,test.end(),next_token))
reversing them and updating the current position
std::reverse(pos,it);
pos=it;
and not forgetting the leftover token.