Reading characters displays all characters followed by some rubbish characters - c++

I have a program that reads a .DAT file that contains a list of:
removepeer 452
addpeer 6576
removepeer 54245
At some point, it reads out rubbish text: H�
Here is a part of my code where I find fault in:
getline(abc, info, '\n'); //data here displays pretty fine
int count = info.size();
char text[count];
for(int a=0; a<count; a++){
text[a] = data[a];
}
cout << text << endl; //Some rubbish text found in some printout!
It prints out the last line followed by some rubbish text

The text array will not be null terminated, which is required when using operator<< with char[] as they are treated as null terminated c-style strings. Random characters from memory will be written until a null terminator is by chance located. Technically, accessing beyond the bounds of an array is undefined behaviour.
To correct, append a null terminator to text. As your compiler has as an extension for variable length arrays (which are not standard C++, but are in C99) you could change it to:
char text[count + 1];
// snip...
text[count] = 0;
Having said that, I am unsure why you are copying from a std::string instance to a char[]. std::string instances also be written to streams using operator<<.

Related

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.

Why does a C++ string need a \0?

I was hoping that I could get some further explanation. I was told that I need to explicitly add \0 to the end of a string. Apparently this is for the C++ string class and that it is actually an array of characters that seems to be parsed under the hood. I was told that we must use the \0 in order to tell where the end of the string is as seen below:
int main()
{
char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
cout << str << endl;
return 0;
}
However, if I have a user input their name, for example, I don't believe that C++ automatically uses the \0 to terminate the string. So the argument that the \0 must be there to know where the string ends makes no sense. Why cant we use the .length() function to account for the length of the string?
I wrote the following program to illustrate that the length of the input can be found from the .length() function.
int main()
{
string firstName;
cout << "Enter your first name: ";
cin >> firstName;
cout << "First Name = " << firstName << endl;
cout << "String Length = " << firstName.length() << endl;
return 0;
}
So, if the user inputs the name "Tom". Then the output would be the following:
First Name = Tom
String Length = 3
I brought this to my professor's attention and also this article http://www.cplusplus.com/reference/string/string/length/
and I was told that is why I am in college because it cannot be done this way. Can any one offer any insight, since I don't understand what I am missing?
The "C string" was adopted into C++ from the C language. The C language did not have a string type. Strings in C were represented as an array of char, and the string was terminated with the NUL byte (\0). A plain string literal in C++ still has these semantics.
The C++ string type maintains the length within the object, as you say, so in a string, the NUL is not required. To get a "C string" from a string, you can use the c_str() method on the string. This is useful if you need to pass the contents of the C++ string to a function that only understands the NUL terminated variety.
std::string s("a string"); // s is initialized,
// the length is computed when \0 is encountered.
assert(s.size() == sizeof("a string")-1);
// sizeof string literal includes the \0
assert(s.c_str()[s.size()] == '\0');
// c_str() includes the \0
In your first program, you are initializing an array of char with an initializer list. The initialization is equivalent to the following:
char str[6] = "Hello";
This style of initializing an array of char is a special allowance that C++ provides since it is the syntax accepted by C.
In your second program, you are getting the name from the standard input. When C++ scans the input to populate the string argument, it essentially scans byte by byte until it encounters a separator (whitespace characters, by default). It may or may not insert a NUL byte at the end.
You're not missing anything per se. The null terminator is used on character arrays to indicate the end. However, the string class takes care of all of that for you. The length attribute is a perfectly acceptable way of doing it since you're using strings.
However, if you're using a character array, then yes, you would need to check if you're on the null terminator, as you may not know the length of your string.
The following will give you no issues.
int length = 2;
char str[] = "AB";
However, try the following, and you'll see some issues.
int length = 5;
char str[length + 1] = "ABCDE"; // +1 makes room for automatic \0
char str2[length + 1] = "ABC";
Try the second snipped using your for loop method knowing the length, and the first one will give you ABCDE, but the second one will give you "ABC" followed by one junk character. It's only one because you'll have [A][B][C][\0][JUNK] in your array. Make length larger and you'll see more junk.

How could I copy data that contain '\0' character

I'm trying to copy data that conatin '\0'. I'm using C++ .
When the result of the research was negative, I decide to write my own fonction to copy data from one char* to another char*. But it doesn't return the wanted result !
My attempt is the following :
#include <iostream>
char* my_strcpy( char* arr_out, char* arr_in, int bloc )
{
char* pc= arr_out;
for(size_t i=0;i<bloc;++i)
{
*arr_out++ = *arr_in++ ;
}
*arr_out = '\0';
return pc;
}
int main()
{
char * out= new char[20];
my_strcpy(out,"12345aa\0aaaaa AA",20);
std::cout<<"output data: "<< out << std::endl;
std::cout<< "the length of my output data: " << strlen(out)<<std::endl;
system("pause");
return 0;
}
the result is here:
I don't understand what is wrong with my code.
Thank you for help in advance.
Your my_strcpy is working fine, when you write a char* to cout or calc it's length with strlen they stop at \0 as per C string behaviour. By the way, you can use memcpy to copy a block of char regardless of \0.
If you know the length of the 'string' then use memcpy. Strcpy will halt its copy when it meets a string terminator, the \0. Memcpy will not, it will copy the \0 and anything that follows.
(Note: For any readers who are unaware that \0 is a single-character byte with value zero in string literals in C and C++, not to be confused with the \\0 expression that results in a two-byte sequence of an actual backslash followed by an actual zero in the string... I will direct you to Dr. Rebmu's explanation of how to split a string in C for further misinformation.)
C++ strings can maintain their length independent of any embedded \0. They copy their contents based on this length. The only thing is that the default constructor, when initialized with a C-string and no length, will be guided by the null terminator as to what you wanted the length to be.
To override this, you can pass in a length explicitly. Make sure the length is accurate, though. You have 17 bytes of data, and 18 if you want the null terminator in the string literal to make it into your string as part of the data.
#include <iostream>
using namespace std;
int main() {
string str ("12345aa\0aaaaa AA", 18);
string str2 = str;
cout << str;
cout << str2;
return 0;
}
(Try not to hardcode such lengths if you can avoid it. Note that you didn't count it right, and when I corrected another answer here they got it wrong as well. It's error prone.)
On my terminal that outputs:
12345aaaaaaa AA
12345aaaaaaa AA
But note that what you're doing here is actually streaming a 0 byte to the stdout. I'm not sure how formalized the behavior of different terminal standards are for dealing with that. Things outside of the printable range can be used for all kinds of purposes depending on the kind of terminal you're running... positioning the cursor on the screen, changing the color, etc. I wouldn't write out strings with embedded zeros like that unless I knew what the semantics were going to be on the stream receiving them.
Consider that if what you're dealing with are bytes, not to confuse the issue and to use a std::vector<char> instead. Many libraries offer alternatives, such as Qt's QByteArray
Your function is fine (except that you should pass to it 17 instead of 20). If you need to output null characters, one way is to convert the data to std::string:
std::string outStr(out, out + 17);
std::cout<< "output data: "<< outStr << std::endl;
std::cout<< "the length of my output data: " << outStr.length() <<std::endl;
I don't understand what is wrong with my code.
my_strcpy(out,"12345aa\0aaaaa AA",20);
Your string contains character '\' which is interpreted as escape sequence. To prevent this you have to duplicate backslash:
my_strcpy(out,"12345aa\\0aaaaa AA",20);
Test
output data: 12345aa\0aaaaa AA
the length of my output data: 18
Your string is already terminated midway.
my_strcpy(out,"12345aa\0aaaaa AA",20);
Why do you intend to have \0 in between like that? Have some other delimiter if yo so desire
Otherwise, since std::cout and strlen interpret a \0 as a string terminator, you get surprises.
What I mean is that follow the convention i.e. '\0' as string terminator

seekg() not working as expected

I have a small program, that is meant to copy a small phrase from a file, but it appears that I am either misinformed as to how seekg() works, or there is a problem in my code preventing the function from working as expected.
The text file contains:
//Intro
previouslyNoted=false
The code is meant to copy the word "false" into a string
std::fstream stats("text.txt", std::ios::out | std::ios::in);
//String that will hold the contents of the file
std::string statsStr = "";
//Integer to hold the index of the phrase we want to extract
int index = 0;
//COPY CONTENTS OF FILE TO STRING
while (!stats.eof())
{
static std::string tempString;
stats >> tempString;
statsStr += tempString + " ";
}
//FIND AND COPY PHRASE
index = statsStr.find("previouslyNoted="); //index is equal to 8
//Place the get pointer where "false" is expected to be
stats.seekg(index + strlen("previouslyNoted=")); //get pointer is placed at 24th index
//Copy phrase
stats >> previouslyNotedStr;
//Output phrase
std::cout << previouslyNotedStr << std::endl;
But for whatever reason, the program outputs:
=false
What I expected to happen:
I believe that I placed the get pointer at the 24th index of the file, which is where the phrase "false" begins. Then the program would've inputted from that index onward until a space character would have been met, or the end of the file would have been met.
What actually happened:
For whatever reason, the get pointer started an index before expected. And I'm not sure as to why. An explanation as to what is going wrong/what I'm doing wrong would be much appreciated.
Also, I do understand that I could simply make previouslyNotedStr a substring of statsStr, starting from where I wish, and I've already tried that with success. I'm really just experimenting here.
The VisualC++ tag means you are on windows. On Windows the end of line takes two characters (\r\n). When you read the file in a string at a time, this end-of-line sequence is treated as a delimiter and you replace it with a single space character.
Therefore after you read the file you statsStr does not match the contents of the file. Every where there is a new line in the file you have replaced two characters with one. Hence when you use seekg to position yourself in the file based on numbers you got from the statsStr string, you end up in the wrong place.
Even if you get the new line handling correct, you will still encounter problems if the file contains two or more consecutive white space characters, because these will be collapsed into a single space character by your read loop.
You are reading the file word by word. There are better methods:
while (getline(stats, statsSTr)
{
// An entire line is read into statsStr.
std::string::size_type posn = statsStr.find("previouslyNoted=");
// ...
}
By reading entire text lines into a string, there is no need to reposition the file.
Also, there is a white-space issue when reading by word. This will affect where you think the text is in the file. For example, white space is skipped, and there is no telling how many spaces, newlines or tabs were skipped.
By the way, don't even think about replacing the text in the same file. Replacement of text only works if the replacement text has the same length as the original text in the file. Write to a new file instead.
Edit 1:
A better method is to declare your key strings as array. This helps with positioning pointers within a string:
static const char key_text[] = "previouslyNoted=";
while (getline(stats, statsStr))
{
std::string::size_type key_position = statsStr.find(key_text);
std::string::size_type value_position = key_position + sizeof(key_text) - 1; // for the nul terminator.
// value_position points to the character after the '='.
// ...
}
You may want to save programming type by making your data file conform to an existing format, such as INI or XML, and using appropriate libraries to parse them.

Weird problem with string function

I'm having a weird problem with the following function, which returns a string with all the characters in it after a certain point:
string after(int after, string word) {
char temp[word.size() - after];
cout << word.size() - after << endl; //output here is as expected
for(int a = 0; a < (word.size() - after); a++) {
cout << word[a + after]; //and so is this
temp[a] = word[a + after];
cout << temp[a]; //and this
}
cout << endl << temp << endl; //but output here does not always match what I want
string returnString = temp;
return returnString;
}
The thing is, when the returned string is 7 chars or less, it works just as expected. When the returned string is 8 chars or more, then it starts spewing nonsense at the end of the expected output. For example, the lines
cout << after(1, "12345678") << endl;
cout << after(1, "123456789") << endl;
gives an output of:
7
22334455667788
2345678
2345678
8
2233445566778899
23456789�,�D~
23456789�,�D~
What can I do to fix this error, and are there any default C++ functions that can do this for me?
Use the std::string::substr library function.
std::string s = "12345678";
std::cout << s.substr (1) << '\n'; // => 2345678
s = "123456789";
std::cout << s.substr (1) << '\n'; // 23456789
The behavior you're describing would be expected if you copy the characters into the string but forget to tack a null character at the end to terminate the string. Try adding a null character to the end after the loop, and make sure you allocate enough space (one more character) for the null character. Or, better, use the string constructor overload which accepts not just a char * but also a length.
Or, even better std::string::substr -- it will be easier and probably more efficient.
string after(int after, string word) {
return word.substr (after);
}
BTW, you don't need an after method, since exactly what you want already exists on the string class.
Now, to answer your specific question about why this only showed up on the 8th and later characters, it's important to understand how "C" strings work. A "C" string is a sequence of bytes which is terminated by a null (0) character. Library functions (like the string constructor you use to copy temp into a string instance which takes a char *) will start reading from the first character (temp[0]) and will keep reading until the end, where "the end" is the first null character, not the size of the memory allocation. For example, if temp is 6 characters long but you fill up all 6 characters, then a library function reading that string to "the end" will read the first 6 characters and then keep going (past the end of the allocated memory!) until it finds a null character or the program crashes (e.g. due to trying to access an invalid memory location).
Sometimes you may get lucky: if temp was 6 characters long and the first byte in memory after the end of your allocation happened to be a zero, then everything would work fine. If however the byte after the end of your allocation happened to be non-zero, then you'd see garbage characters. Although it's not random (often the same bytes will be there every time since they're filled by operations like previous method calls which are consistent from run to run of your program), but if you're accessing uninitialized memory there's no way of knowing what you'll find there. In a bounds checking environment (e.g. Java or C# or C++'s string class), an attempt to read beyond the bounds of an allocation will throw an exception. But "C" strings don't know where their end is, leaving them vulnerable to problems like the one you saw, or more nefarious problems like buffer overflows.
Finally, a logical follow-up question you'd probably ask: why exactly 8 bytes? Since you're trying to access memory that you didn't allocate and didn't initialize, whats in that RAM is what the previous user of that RAM left there. On 32-bit and 64-bit machines, memory is generally allocated in 4- or 8-byte chunks. So it's likely that the previous user of that memory location stored 8 bytes of zeroes there (e.g. one 64-bit integer zero) zeros there. But the next location in memory had something different left there by the previous user. Hence your garbage characters.
Moral of the story: when using "C" strings, be very careful about your null terminators and buffer lengths!
Your string temp is not NULL terminated. You requite temp[a] = '\0'; at the end of loop. Also you need to allocate word.size() - after + 1 chars so as to accomodate the NULL character.
You're not null-terminating your char array. C-style strings (i.e., char arrays) need to have a null character (i.e., '\0') at the end so functions using them know when to stop.
I think this is basically your after() function, modulo some fudging of indexes:
string after(int after, string word) {
return word.substring(after);
}