I'm simply trying to read a file using fread and output the contents. It's partially working. It outputs everything correctly but it ends with a bunch of random characters.
#include <iostream>
using namespace std;
void ReadFile(char* filename,char*& buffer)
{
FILE *file = fopen(filename,"rb");
fseek(file,0,SEEK_END);
int size = ftell(file);
rewind(file);
buffer = new char[size];
memset(buffer,0,size);
int r = fread(buffer,1,size,file);
cout << buffer;
fclose(file);
}
int main()
{
char* buffer;
ReadFile("test.txt",buffer);
cin.get();
}
Let's say 'size' is 50 in this instance. for some reason, the size of buffer ends up 55 or 56 after fread is called. I emptied the buffer before using it and tried outputting it, everything is normal (it's empty). Right after the call to fread the buffer somehow gets bigger and is filled with random characters. I've opened the text file in a hex editor to ensure there isn't anything I'm not seeing but there isn't. The file is 50 bytes. fread returns the amount of bytes read, in this case returned to 'r', 'r' is what it should be. so where the mother eff are these bytes coming from?
simplified: fread returns correct amount of bytes read but the buffer somehow makes itself bigger after fread is called, then fills it with random characters. why?
I can't for the life of me figure out how this is happening.
Also, before anyone gives me an easy fix, I already know I could just do buffer[r] = '\0' and not have it output anymore random characters but I'd much rather know WHY this is happening.
cout's << operator on char* expects C strings, so you need to null-terminate your buffer:
int size = ftell(file)+1; // Leave space for null terminator
...
int r = fread(buffer,1,size-1,file);
buffer[r] = '\0';
cout << buffer;
The extra characters that you see is random data in the memory addresses after the end of your buffer. operator << does not know that the string has ended, so it continues printing until it finds the first '\0' byte.
You probably just forgot about null terminating the buffer. Instead, use cout.write and supply the length of the buffer:
Adding a bit of error handling (not enough, but a start), missing includes and using statements: http://coliru.stacked-crooked.com/view?id=8bc4f3b7111554c705de96450d806104-f674c1a6d04c632b71a62362c0ccfc51
#include <iostream>
#include <string>
#include <vector>
#include <cstring>
using namespace std;
void ReadFile(const char* filename,char*& buffer)
{
FILE *file = fopen(filename,"rb");
if (!file)
return;
fseek(file,0,SEEK_END);
int size = ftell(file);
rewind(file);
buffer = new char[size];
memset(buffer,0,size);
int r = fread(buffer,1,size,file);
cout.write(buffer, r);
fclose(file);
}
int main()
{
char* buffer;
ReadFile("test.txt",buffer);
cin.get();
}
Actually cout will print the string until it does not get any NULL char. That mean it needed a NULL for termination.
But assign a NULL is not a good solution for all the times. Your data might be a binary at that time cout will print only the output up to the NULL char. I mean binary data could be anything and it could be a not readable char also. and cout will consider it as a NULL char. That's why it always safe to use a for loop up to the length of the string or your dataset.
len = strlen(buffer)
for (int i = 0; i < len; i++)
printf("%c", buffer[i])
//or you could use FILE *fp; for (int i= 0; i < len; i++) fprintf(fp, "%c", buffer[i]); another good approach is to use fwrite.
Related
I have an Arduino that controls timers. The settings for timers are stored in byte arrays. I need to convert the arrays to strings to SET a string on an external Redis server.
So, I have many arrays of bytes of different lengths that I need to convert to strings to pass as arguments to a function expecting char[]. I need the values to be separated by commas and terminated with '\0'.
byte timer[4] {1,5,23,120};
byte timer2[6] {0,0,0,0,0,0}
I have succeeded to do it manually for each array using sprintf() like this
char buf[30];
for (int i=0;i<5;i++){ buf[i] = (int) timer[i]; }
sprintf(buf, "%d,%d,%d,%d,%d",timer[0],timer[1],timer[2],timer[3],timer[4]);
That gives me an output string buf: 1,5,23,120
But I have to use a fixed number of 'placeholders' in sprintf().
I would like to come up with a function to which I could pass the name of the array (e.g. timer[]) and that would build a string, probably using a for loop of 'variable lengths' (depending of the particular array to to 'process') and many strcat() functions. I have tried a few ways to do this, none of them making sense to the compiler, nor to me!
Which way should I go looking?
Here is the low tech way you could do it in normal C.
char* toString(byte* bytes, int nbytes)
{
// Has to be static so it doesn't go out of scope at the end of the call.
// You could dynamically allocate memory based on nbytes.
// Size of 128 is arbitrary - pick something you know is big enough.
static char buffer[128];
char* bp = buffer;
*bp = 0; // means return will be valid even if nbytes is 0.
for(int i = 0; i < nbytes; i++)
{
if (i > 0) {
*bp = ','; bp++;
}
// sprintf can have errors, so probably want to check for a +ve
// result.
bp += sprintf(bp, "%d", bytes[i])
}
return buffer;
}
an implementation, assuming that timer is an array (else, size would have to be passed as a parameter) with the special handling of the comma.
Basically, print the integer in a temp buffer, then concatenate to the final buffer. Pepper with commas where needed.
The size of the output buffer isn't tested, mind.
#include <stdio.h>
#include <strings.h>
typedef unsigned char byte;
int main()
{
byte timer[4] = {1,5,23,120};
int i;
char buf[30] = "";
int first_item = 1;
for (i=0;i<sizeof(timer)/sizeof(timer[0]);i++)
{
char t[10];
if (!first_item)
{
strcat(buf,",");
}
first_item = 0;
sprintf(t,"%d",timer[i]);
strcat(buf,t);
}
printf(buf);
}
I am in need of some help with this program. I am in my first ever programming class and have run into wall trying to getting my program to work. I have included what I have written so far but still it doesn't compile. It is giving the error: argument list for class template "std::vector" is missing.
Here is the question:
When you read a long document, there is a good chance that many words occur multiple times. Instead of storing each word, it may be beneficial to only store unique words, and to represent the document as a vector of pointers to the unique words. Write a program that implements this strategy. Read a word at a time from cin. Keep a vector <char *> of words. If the new word is not present in this vector, allocate memory, copy the word into it, and append a pointer to the new memory. If the word is already present, then append a pointer to the existing word.
Below is code snippet:
#include "stdafx.h"
#include <string>
#include <iostream>
using namespace std;
/* Create a vector of char pointers to hold the individual words.
Create a string input to hold the next input through cin. */
int main() {
vector words;
string input;
/* Keep the while loop running using cin as the condition to read an entire document.
This will end when a document has reached its end. */
while (cin >> input) {
/* For every word read as a string, convert the word into a c-string by allocating
a new character array with the proper size and using c_str and strcpy to copy
an identical c-string into the memory heap. */
char* temp = new char[input.length() + 1];
strcpy(temp, input.c_str());
/* Next, check if the word is already in the words array. Use a boolean variable
that updates if the word is found. Compare words by using the strcmp function;
when they are equal, strcmp equals 0. */
bool already_present = false;
for (int i = 0; i < words.size(); i++) {
if (strcmp(temp, words[i]) == 0) {
already_present = true;
}
}
/* If the word is already present, delete the allocated memory.
Otherwise, push the pointer into the words vector. */
if (already_present) {
delete temp;
} else {
words.push_back(temp);
}
}
}
I hope below code snippet could be helpful:
#include <string>
#include <iostream>
#include <string.h> // String.h for strcmp()
#include <vector> // Vector Header file is added
using namespace std;
int main() {
vector <char *> words; // vector of char *
string input;
while (cin >> input) {
char *temp = new char[input.length() + 1];
strcpy(temp, input.c_str());
bool already_present = false;
for (unsigned int i = 0; i < words.size(); i++) {
if (strcmp(temp, words[i]) == 0) {
already_present = true;
}
}
if (already_present) {
delete temp;
} else {
words.push_back(temp);
}
}
/* Print the desired output */
for(unsigned int i=0; i<words.size(); i++) {
cout << words[i] << endl;
}
return 0;
}
Any doubt, comments most welcome.
EDIT: After reading your comments, I came to the conclusion that you use Microsoft Visual Stdio. See, the reason you were getting warning is that strcpy() is potentially unsafe because it can lead to buffer overflow if you try to copy a string to a buffer that is not large enough to contain it.
Consider a code snippet for a moment:
char foo[10]; /* a buffer able to hold 9 chars (plus the null) */
char bar[] = "A string longer than 9 chars";
strcpy( foo, bar ); /* compiles ok, but VERY BAD because you have a buffer overflow
and are corrupting memory. */
strcpy_s() is safer because you have to explicitly specify the size of the target buffer, so the function will not overflow:
strcpy_s( foo, 10, bar ); /* strcpy_s will not write more than 10 characters */
The limitations of this strcpy_s() is that, it is non-standard and MS specific. Therefore if you write code to use it, your code will not be portable any more.
So, what I want to do is placing a 'p' before all vowels, I'm doing that but because of the memmove, the char array is filled with strange symbols.
#include<conio.h>
#include <cstring>
#include <iostream>
using namespace std;
int main()
{
char s1[256];
cin.get(s1,255);
cin.get();
for(int i=0;i<strlen(s1);i++)
{
if(strchr("aeiou",s1[i])){
memmove(s1+i+1,s1+i,strlen(s1));
// duplicating the vowel so i can replace it
s1[i]='p';
i++;
}
}
cout<<s1;
getch();
return 0;
}
Input : oaie
In your memmove call, you are using the incorrect length:
memmove(s1+i+1,s1+i,strlen(s1));
strlen(s1) is the length of the string starting from the beginning and not including the null terminator. So there are two problems. First, you want the length of the string starting from the current position. Second, you want the length including the null terminator, so that the null terminator gets copied.
The result of these bugs is that if the first character is a vowel then the memove will over-write the null terminator and additional garbage in the buffer will start being processed as if it were part of your string.
Additionally memove will access outside the buffer given certain input and garbage in the buffer.
The solution is to use the correct length, strlen(s1+i), and to include the null terminator:
memmove(s1+i+1,s1+i,strlen(s1+i)+1);
cin.get(s1, 255) does store a null terminator, and in fact you can safely give it the correct buffer size: cin.get(s1, 256).
using std::string you can avoid these sorts of mistakes:
#include <cstring>
#include <string>
#include <iostream>
int main() {
std::string s1;
std::getline(std::cin, s1);
for (int i = 0; i < s1.size(); ++i) {
if (std::strchr("aeiou", s1[i])) {
s1.insert(i, "p");
++i;
}
}
std::cout << s1 << '\n';
}
Note that this doesn't have any fixed buffer size, doesn't have to give special consideration to null terminators, doesn't have to figure out how many bytes need moving, and directly expresses the intent to "insert 'p' at this location".
I would like to read unlimited length line of text from the input. I wish not to use string or any of it.
I tried that but it seems does not work. I always get the same output.
I do not want to use: string, the input has to be of any length.
#include <iostream>
#include <stdio.h>
using namespace std;
char * getline(){
char * buf = new char[10];
int result = scanf("%as\n", &buf);
if (result < 0) {
if (NULL != buf)
delete []buf;
return NULL;
}
return buf;
}
int main(){
char* haha = getline();
cout <<haha << endl;
return 0;
}
char * buf; just creates the pointer variable, but does not allocate memory.
use: buf = new char[...].
Obviously, since your computer has not got unlimited memory, you will need to specify a size instead of ...
And delete it as: delete [] buf
But, I think the best solution is:
std::string s;
std::cin >> s;
You can't read into a buffer that doesn't exist! Here buf points to an undefined location in memory, and the most likely outcome of this code is a crash. You must allocate some space (using new[]) before you can read data into it; and of course, that space must be of some well-defined size. You can always allocate a new, large buffer at any time, and copy the contents of the smaller one into it; this is how you "grow" memory blocks in C++, and it's how string works internally.
Hi I'm trying to tokenize a string by loading an entire file into a char[] using fread.
For some strange reason it is not always working, and valgrind complains in this very small sample program.
Given an input like test.txt
first
second
And the following program
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
//returns the filesize in bytes
size_t fsize(const char* fname){
struct stat st ;
stat(fname,&st);
return st.st_size;
}
int main(int argc, char *argv[]){
FILE *fp = NULL;
if(NULL==(fp=fopen(argv[1],"r"))){
fprintf(stderr,"\t-> Error reading file:%s\n",argv[1]);
return 0;
}
char buffer[fsize(argv[1])];
fread(buffer,sizeof(char),fsize(argv[1]),fp);
char *str = strtok(buffer," \t\n");
while(NULL!=str){
fprintf(stderr,"token is:%s with strlen:%lu\n",str,strlen(str));
str = strtok(NULL," \t\n");
}
return 0;
}
compiling like
gcc test.c -std=c99 -ggdb
running like
./a.out test.txt
thanks
Your buffer size should be filesize + 1. The +1 is for the null char.
filesize = fsize(argv[1]);
char buffer[filesize + 1];
Also fread does not put a \0 at the end of the string. So you'll have to do it yourself as:
fread(buffer,sizeof(char),filesize,fp);
buffer[filesize] = 0;
From this site:
int main(int argc, char* argv[])
{
std::string str = "The quick brown fox";
// construct a stream from the string
std::istringstream stream(str);
// use stream iterators to copy the stream to the vector
// as whitespace separated strings
std::istream_iterator<std::string> it(stream), end;
std::vector<std::string> results(it, end);
// results = ["The", "quick", "brown", "fox"]
}
SO much easier than dealing with those nasty C-strings that keep banging you on the head.
And you know what's great about using higher-order methods ? It takes less screen estate and is easier to understand.
buffer is not null-terminated. You need to make it one byte larger than the size of the file, and you need to set the last byte to be \0.
Your buffer must be filesize + 1 and you will also need to set the terminating 0:
int size = fsize(argv[1]);
char buffer[size + 1];
buffer[size] ='\0';
Also, you should probably allocate the buffer on the heap instead of the stack...
Your buffer is too small. Try this:
int fileSize = fsize(argv[1]);
char buffer[fileSize + 1];
buffer[fileSize] = 0;
right before your call to fread.