How to make this function stop at N characters/ - c++

So I have this assignment which is to take the contents of one text file and copy it to another. We have to rewrite the strncpy default function, but slightly different. Here's what the lab write up states explicitly:
This function replaces the std::strncpy function. That is, it copies the content of s2 to s1 but no more than N characters. Hint: Since
strlength (and std::strlen) doesn't count the null-terminating
character, remember to add increment that number by one when calling
strncopy (and std::strncpy). To avoid producing a segmentation
violation, you must check if the null-terminating character has been
encountered before you dereference s2 to copy the data to s1. Again,
base your code on a pointer loop.
Here's what I have so far for my function:
void strncopy(char *s1, const char *s2, int N){
// return if no memory is allocated to the s1
if (s1 == NULL)
NULL;
// take a pointer pointing to the beginning of s1 string
char *ptr = s1;
// copy the C-string pointed by s2 into the array
// pointed by s1
while (*s2 != '\0'){
if (*s1 == N){
break;
}
// *s1 = *s2;
s1++;
s2++;
}
// include the terminating null character
*s1 = '\0';
}
The problem that it appears to have is the loop is not stopping when it reaches N amount of characters.
Here's the bit of my main function implementing this function:
int main(int argc, char *argv[]) {
// check for four command line arguments
// if not, print error message and bail out
if (argc != 4){
cerr<<"Error";
return -1;;
}
// declare two streams: file1, file2
fstream file1;
fstream file2;
// declare two char text line arrays
char textline1[256];
char textline2[256];
if (strcompare(argv[1], "-copy") == 0) {
//open file1 for reading
file1.open(argv[2]);
//open file2 for writing
file2.open(argv[3]);
//read text line 1 frome file1
while (file1.getline(textline1, 256)) {
// determine length of textline1
char t1 = strlength(textline1);
// copy textline1 to textline2
strncopy(textline2, textline1, (t1+1));
// write textline2 + newline to file2
file2 << textline2 << '\n';
}
}
Here's the text in the file I'm trying to copy:
Captain's log, stardate 3192.1. The Enterprise is en route to star
cluster NGC 321. Objective, to open diplomatic relations with the
civilisations known to be there. We have sent a message to Eminiar
Seven, principal planet of the star cluster, informing them of our
friendly intentions. We are awaiting an answer.
And here's what I'm actually copying:
Captain's log, stardate 3192.1. The Enterprise is en route to star
cluster NGC 321. Objective, to open diplomatic relations with the
civilisations known to be there. We have sent a message to Eminiar
Seven, principal planet of the star cluster, informing them of our
friendly intentions. We are awaiting an answer. n answer.cluster, .
Does anyone know what my problem is?
I'm sorry this is a dumb question, but ever since classes went to online only, its getting really difficult to get help from instructor and TAs.

Currently you have the following to check the length:
if (*s1 == N){
break;
}
However, since s1 wouldn't contain the length in it (presumably), you are comparing the characters in s1 to N, which will likely be false (but is UB if s1 was not initialized).
You can modify your while condition like so:
while (*s2 != '\0' && N--)
When N reaches 0, it's condition will be false, and hence stop the loop. This assumes that N is greater than or equal to zero to begin with.
Also, to add, you have
// return if no memory is allocated to the s1
if (s1 == NULL)
NULL;
This does not return, it actually does nothing. You want to do the following:
if ( s1 == NULL )
return;
PS: You may want to consider using nullptr instead of NULL if you're using C++11 or later.

Related

Why a "no matching function" error for call by reference with literal number?

The problem asks to create a program that asks the user to enter some text and that text will be surrounded by asterisks depending on the width of the screen for example if the user inputs "Hello world" the output should be:
****************
* Hello World! *
****************
I've tried to create the functions but I'm stuck becaus of a compiler error with the shown minimal code.
Question: Why does it tell me no matching function for within_width(text, 80)?
Some of the code I have is below:
#include <iostream>
#include <string>
void display_header (std::string &header) {
std::string text;
header = text;
}
bool within_width (std::string& text, unsigned short int& max_width) {
}
int main() {
std::string text;
std::cout << "Please enter header text: ";
std::getline(std::cin, text);
if (within_width(text, 80)) {
// call the display_header function and pass in the text
// inputted by the user
} else {
std::cout << text;
}
return 0;
}
This declaration of the function
bool within_width (std::string& text, unsigned short int& max_width)
asks for an unsigned short int variable, because it has a reference parameter, see the second &.
To satisfy it, you need to put the value 80 into a variable and give the variable as parameter.
unsigned short int MyWidth=80;
if (within_width(text, MyWidth))
Alternatively (but I assume you are not allowed) you can use a call by value parameter
bool within_width (std::string& text, unsigned short int max_width)
Then you could call as shown.
I won't give a full answer to the exercise here, just some clues.
the display_header() and within_width() functions need to know the string given in parameters but may not modify it ; thus the type of this parameter should be const std::string & (the const was missing).
the second parameter of the within_width() function is just an integer that will be compared to the length of the string ; you don't need to pass it by reference (or at least const), rather by value. Here, the (non-const) reference prevents from passing the literal constant 80.
(it seems to be the main concern of the question after edition)
You need to reason step by step.
all of this depends on the size of the string (12 for Hello World!) ; this information is available via size(text) (or text.size())
(https://en.cppreference.com/w/cpp/iterator/size)
(https://en.cppreference.com/w/cpp/string/basic_string/size)
This size will have to be compared to max_width
Displaying the line with header will require 4 more characters because * will be prepended and * will be appended.
Thus the two surrounding lines will have the length size(header)+4 too.
In order to create such a string made of *, you could use a constructor of std::string taking two parameters : the count of characters and the character to be repeated.
(https://en.cppreference.com/w/cpp/string/basic_string/basic_string)
Send all of this to std::cout in the correct order.
Edit: Just noticing that this answer probably goes far beyond the scope of the task you have been given (just filling in some skeleton that has been provided by your teacher).
I'll still leave it here to illustrate what could be done with arbitrary input. Maybe you want to experiment a little further than what you have been asked...
bool within_width(...)
Pretty simple: string.length() <= max – just wait a second, you need to consider asterisks and spaces at beginning and end of output, so: max - 4
But you can do better, you can split the string, best at word boundaries. That's a bit difficult more difficult, though:
std::vector<std::string> lines;
// we'll be starting with an initially empty line:
auto lineBegin = text.begin();
auto lineEnd = text.begin();
for(auto i = text.begin(); i != text.end(); ++)
// stop condition empty: we'll stop from inside the loop...
{
// ok, we need to find next whitespace...
// we might try using text.find_first_of("..."), but then we
// need to know any whitespace characters ourselves, so I personally
// would rather iterate manually and use isspace function to determine;
// advantage: we can do other checks at the same time, too
auto distance = std::distance(lineBegin, i);
if(std::distance(lineBegin, i) > maxLineLength)
{
if(lineEnd == lineBegin)
{
// OK, now we have a problem: the word itself is too long
// decide yourself, do you want to cut the word somewhere in the
// middle (you even might implement syllable division...)
// or just refuse to print (i. e. throw an exception you catch
// elsewhere) - decide yourself...
}
else
{
lines.emplace_back(lineBegin, lineEnd);
lineBegin = lineEnd; // start next line...
}
}
// OK, now handle current character appropriately
// note: no else: we need to handle the character in ANY case,
// if we terminated the previous line or not
if(std::isspace(static_cast<unsigned char>(*i)))
{
lineEnd = i;
}
// otherwise, we're inside a word and just go on
}
// last line hasn't been added!
lines.emplace_back(lineBegin, lineEnd);
Now you can calculate maximum length over all the strings contained. Best: Do this right when adding a new line to the vector, then you don't need a separate loop...
You might have noticed that I didn't remove whitespace at the end of the strings, so you wouldn't need to add you own one, apart, possibly, from the very last string (so you might add a lines.back() += ' ';).
The ugly part, so far, is that I left multiple subsequent whitespace. Best is removing before splitting into lines, but be aware that you need to leave at least one. So:
auto end = text.begin();
bool isInWord = false; // will remove leading whitespace, if there is
for(auto c : text)
{
if(std::isspace(static_cast<unsigned char>(c)))
{
if(isInWord)
{
*end++ = ' '; // add a single space
isInWord = false;
}
}
else
{
*end++ = c;
isInWord = true;
}
}
This would have moved all words towards the beginning of the string, but we yet to drop the surplus part of the string yet contained:
text.erase(end, text.end());
Fine, the rest is pretty simple:
iterate over maximum length, printing a single asterisk in every loop
iterate over all of your strings in the vector: std::cout << "* " << line << "*\n";
repeat the initial loop to print second line of asterisks
Finally: You introduced a fix line limit of 80 characters. If console is larger, you just won't be using the entire available width, which yet might be acceptable, if it is smaller, you will get lines broken at the wrong places.
You now could (but that's optional) try to detect the width of the console – which has been asked before, so I won't go any deeper into.
Final note: The code presented above is untested, so no guarantee to be bugfree!

using getchar() and getting stuck in loop [duplicate]

For my homework assignment, I need to implement Horners Algorithm for converting between bases.
I have been told to use getchar() for this assignment. But I am having a problem where when I hit enter, the program doesn't terminate and just takes in more chars.
Example:
bryce> ./pa1
Enter the fromRadix:16
Enter the toRadix:2
abc
abc
^C
bryce>
Code:
int readRadixA(int radixA)
{
char myChar = getchar();
int result = 0;
int run = 0;
while(myChar != EOF)
{
if(myChar == "\n")
break;
Horners();
myChar = getchar();
}
return result;
}
I am not asking for help implementing Horners; I am asking for help to terminate the getchar() correctly.
if(myChar=="\n")
^ ^
You're comparing myChar wrong. Try this instead:
if(myChar == '\n')
^ ^
A second problem is that getchar returns int, not char. Maybe you can rewrite it like this:
int myChar;
while((myChar = getchar()) != EOF && myChar != '\n')
{
/* Your stuff. */
}
EDIT
In light of comments, I think some stdio operation before that while is leaving a \n in the buffer.
Instead of scanf("%d", &radix) try:
scanf("%d ", &radix);
^
That space will make scanf eat the remaining blanks (including the newline).
Check the return type of getchar(). Yes, it's an int. That's because EOF must have a value that can be distinguished from a valid character. myChar must actually be made to be int.
Try this code
int readRadixA(int radixA)
{
char myChar;
int result = 0;
int run = 0;
do
{
myChar = getchar();
// implement horners here
}while(myChar != 13);
return result;
}
I checked your code I think you are leaving a '\n' in the input keyboard buffer after the toRadix.
And their is one more thing that
getchar()
reads all the characters in one go till a '\n' is received.
And there is one more mistake you have committed by comparing a
char to a pointer e.g mychar=="\n"
further information about your implementation of toRadix can be really helpful to answer your question
On linux, to end the standard input, you have to type  Ctrl-D. The kernel and tty layers makes that an end-of-file mark or condition. Then getchar gives EOF (which is not a valid char, for example on systems where char are unsigned bytes between 0 and 255, EOF could be -1).
Notice that feof(3) is valid only after a read operation (e.g. getchar, fgets, etc...) so coding while(feof(stdin)) is generally wrong (contrarily to what I wrote in the previous version of this answer). You'll better test that getchar is returning EOF so your myChar should be an int (not a char).

C++ test last character of a string

I want to test if the var string is terminated with a '\n' or not, in order to detect and remove some incomplete incoming variables from a stream async socket. It seems the following code doesn't work properly. Why ?
string var;
char *rest = nullptr;
char *pVar = nullptr;
istringstream iss(sReadBuffer); // Put into a stream
while (getline(iss, var)) // Default delimiter '\n' or EOF
{
int size = var.size();
pVar = _strdup(var.c_str()); // Cast string to char * for later use (strstr,...)
if(var[size] != '\n') // If incomplete variable found (not newline ended)...
{
debug("Incomplete variable found : ", pVar, "\n");
rest = pVar;
break;
}
//... proceed with variable normally if they are complete
getline() discards the newline. If you want to test for EOF, test the stringstream object itself.
var[size] accesses the string out of bounds. But you can use the std::string::back member to get a reference to the last element.
if(var.back() != '\n')
That avoids potential indexing errors. The code above assumes var is not empty. You can check with std::string::empty(). For example,
if((!var.empty()) && (var.back() != '\n'))
How you expect to find a \n in a string read with getline is another matter.

strcpy() is not copying properly c++

Recently I made a program, it has a character array board[8][8][2];
It is basically meant to be a 8X8 board which can store '2' lettered strings. I am not providing the complete code.
But here is the problem.
for (j = 0; j < 8; j++) {
strcpy(board[1][j], P[j].sym);
}
cout << board[1][1] << endl;
Here P[1].sym="P1" and P[0].sym="P0" and P[2].sym="P2"
Therefore P[j].sym is basically a two letter string and board[1][j] should also be a two letter string.
But the output for
cout << board[1][1] << endl;
is given as P1P2P3P4P5P6P7
and the output for
cout << board[1][0] << endl;
is given as P0P1P2P3P4P5P6P7
For
cout << board[1][5] << endl;
P5P6P7 is the output.
To remove any doubt the whole board[8][8][[2] is already initialised
and all of P[j].sym are already initialised.
If it helps here is the code for the initialisation of P:
#include <iostream>
#include <string.h>
using namespace std;
class Game
{
public:
char board[8][8][2];
char *****possibilities;
};
class Pawn : virtual public Game {
public:
char sym[2];
int possiblec[4][2];
Pawn() { }
Pawn(int i) {
char a[2];
a[0] = 'P';
a[1] = (char)(i + 48);
strcpy(sym, a);
}
};
And here somewhere else in the program I did
Pawn P[8];
It calls the constructor and then later on I called the parameterised contructor explicitly.
for (int i = 0; i < 8; i++) {
P[i] = i;
}
After this I checked for different values of P[j].sym and all of them return the perfect values I wanted.
But not when I'm using strcpy() What is the problem here. This program is just a practice program to get a hang of it.
Character arrays in C++ ( and C ) are terminated with a Null character ('\0' ) . So, even if you need to store just two characters in your string, you must have an extra space to store the Null character.
A character array which does not terminate with a Null character can lead to a lot of other problems. It is a wrong practice.
If your character array does not terminate with a Null character, you will get a lot of problems when you call functions such as strcpy() , strcat() , etc...
So, you should change
char board[8][8][2]
to
char board[8][8][3]
And if you have any other strings just like this one, then leave one extra space in them as well.
The reason your code behaved as such is because you got lucky.
Functions such as strcpy() , strcat() all continue to copy ( or append ) until they encounter a Null Character ( which is numerically equal to zero ). So, it continues to do so until the Null character is encountered. But if there is no Null character, then you will most probably get Undefined Behavior. In your case, you just got lucky.
I will show you a brief working of strcpy() ( from here )
char * strcpy(char p, const char * q) {
while (*p++=*q++);
//there's also a return p; statement at the end
}
That is the function.
the while loop executes until it encounters false, and the equivalent for false is 0. So, when it encounters a Null character ( which is also numerically equal to 0 ), the while loop terminates and the copying is complete, and the function ends. So, if there is no Null character at the end, it will give you undefined Behavior.
You can refer man for more info about them
You should always reserve one extra character because strings in C and C++ are null terminated, which that they need one extra character to sign the end of the string.
So, please, change
board[8][8][2]
to
board[8][8][3]
as well as sym[2] to sym[3], a[2] to a[3] (generally add one to the length of all strings) and try again.
By looking at the manual pages for strcpy:
Copies the C string pointed by source into the array pointed by
destination, including the terminating null character (and stopping at
that point).
This means that that function will stop only when it encounters the null character. That's why it would fail if there wasn't any present. But, by setting one character at a time, there's obviously no such problem visible (it will become visible later on, if you try to execute a function that stops only when it encounters a null character and there are plenty of them).
Strings are null ('\0') terminated in C++. When you pass in an character array to printf it stops printing at the null character. I'm guessing the only reason it stopped printing at P7 is because you got lucky and the next memory location happens to be storing Null. You need to make your char arrays at least 1 character longer than the string you want to store.

Cleaning a string of punctuation in C++

Ok so before I even ask my question I want to make one thing clear. I am currently a student at NIU for Computer Science and this does relate to one of my assignments for a class there. So if anyone has a problem read no further and just go on about your business.
Now for anyone who is willing to help heres the situation. For my current assignment we have to read a file that is just a block of text. For each word in the file we are to clear any punctuation in the word (ex : "can't" would end up as "can" and "that--to" would end up as "that" obviously with out the quotes, quotes were used just to specify what the example was).
The problem I've run into is that I can clean the string fine and then insert it into the map that we are using but for some reason with the code I have written it is allowing an empty string to be inserted into the map. Now I've tried everything that I can come up with to stop this from happening and the only thing I've come up with is to use the erase method within the map structure itself.
So what I am looking for is two things, any suggestions about how I could a) fix this with out simply just erasing it and b) any improvements that I could make on the code I already have written.
Here are the functions I have written to read in from the file and then the one that cleans it.
Note: the function that reads in from the file calls the clean_entry function to get rid of punctuation before anything is inserted into the map.
Edit: Thank you Chris. Numbers are allowed :). If anyone has any improvements to the code I've written or any criticisms of something I did I'll listen. At school we really don't get feed back on the correct, proper, or most efficient way to do things.
int get_words(map<string, int>& mapz)
{
int cnt = 0; //set out counter to zero
map<string, int>::const_iterator mapzIter;
ifstream input; //declare instream
input.open( "prog2.d" ); //open instream
assert( input ); //assure it is open
string s; //temp strings to read into
string not_s;
input >> s;
while(!input.eof()) //read in until EOF
{
not_s = "";
clean_entry(s, not_s);
if((int)not_s.length() == 0)
{
input >> s;
clean_entry(s, not_s);
}
mapz[not_s]++; //increment occurence
input >>s;
}
input.close(); //close instream
for(mapzIter = mapz.begin(); mapzIter != mapz.end(); mapzIter++)
cnt = cnt + mapzIter->second;
return cnt; //return number of words in instream
}
void clean_entry(const string& non_clean, string& clean)
{
int i, j, begin, end;
for(i = 0; isalnum(non_clean[i]) == 0 && non_clean[i] != '\0'; i++);
begin = i;
if(begin ==(int)non_clean.length())
return;
for(j = begin; isalnum(non_clean[j]) != 0 && non_clean[j] != '\0'; j++);
end = j;
clean = non_clean.substr(begin, (end-begin));
for(i = 0; i < (int)clean.size(); i++)
clean[i] = tolower(clean[i]);
}
The problem with empty entries is in your while loop. If you get an empty string, you clean the next one, and add it without checking. Try changing:
not_s = "";
clean_entry(s, not_s);
if((int)not_s.length() == 0)
{
input >> s;
clean_entry(s, not_s);
}
mapz[not_s]++; //increment occurence
input >>s;
to
not_s = "";
clean_entry(s, not_s);
if((int)not_s.length() > 0)
{
mapz[not_s]++; //increment occurence
}
input >>s;
EDIT: I notice you are checking if the characters are alphanumeric. If numbers are not allowed, you may need to revisit that area as well.
Further improvements would be to
declare variables only when you use them, and in the innermost scope
use c++-style casts instead of the c-style (int) casts
use empty() instead of length() == 0 comparisons
use the prefix increment operator for the iterators (i.e. ++mapzIter)
A blank string is a valid instance of the string class, so there's nothing special about adding it into the map. What you could do is first check if it's empty, and only increment in that case:
if (!not_s.empty())
mapz[not_s]++;
Style-wise, there's a few things I'd change, one would be to return clean from clean_entry instead of modifying it:
string not_s = clean_entry(s);
...
string clean_entry(const string &non_clean)
{
string clean;
... // as before
if(begin ==(int)non_clean.length())
return clean;
... // as before
return clean;
}
This makes it clearer what the function is doing (taking a string, and returning something based on that string).
The function 'getWords' is doing a lot of distinct actions that could be split out into other functions. There's a good chance that by splitting it up into it's individual parts, you would have found the bug yourself.
From the basic structure, I think you could split the code into (at least):
getNextWord: Return the next (non blank) word from the stream (returns false if none left)
clean_entry: What you have now
getNextCleanWord: Calls getNextWord, and if 'true' calls CleanWord. Returns 'false' if no words left.
The signatures of 'getNextWord' and 'getNextCleanWord' might look something like:
bool getNextWord (std::ifstream & input, std::string & str);
bool getNextCleanWord (std::ifstream & input, std::string & str);
The idea is that each function does a smaller more distinct part of the problem. For example, 'getNextWord' does nothing but get the next non blank word (if there is one). This smaller piece therefore becomes an easier part of the problem to solve and debug if necessary.
The main component of 'getWords' then can be simplified down to:
std::string nextCleanWord;
while (getNextCleanWord (input, nextCleanWord))
{
++map[nextCleanWord];
}
An important aspect to development, IMHO, is to try to Divide and Conquer the problem. Split it up into the individual tasks that need to take place. These sub-tasks will be easier to complete and should also be easier to maintain.