so I have this function that, for now, takes a string from a input.txt and passes it to a string str by reference using strncpy(). but when I've tried calling the string outside of the function, I dont get anything at all if it tries to copy more than 16 (15 with the '/0' at the end).
this is my function:
int openFile(char *str){
FILE *arquivo;
arquivo = fopen("input.txt", "r");
if (arquivo == NULL){
return 1; // couldnt open input.txt
}
fseek(arquivo, 0, SEEK_END);
int size = ftell(arquivo);
rewind(arquivo);
char *a = malloc(sizeof(char) * size); // enough for 'size' chars
size_t buffer_len = sizeof(char) * size; // sizeof(a) returns sizeof(a as pointer), so uh.. dont
printf("%d %d %d %d\n", sizeof(char), sizeof(a), size, buffer_len); // just to test what it's getting
fgets(a, size + 1, arquivo);
printf( "%s, %d" , a, size);
realloc(str, sizeof(char) * size); // just in case there isnt enough space in str
strncpy(str, a, 16); // error in THIS LINE doesnt copy more than 16 bytes
// memmove(str, a, 16); //not a problem with strncpy, memmove gives the same problem
// str[buffer_len - 1] = '\0';
fclose(arquivo);
free(a);
return 0;
}
my main is really simple too
int main(){
char *str;
if(openFile(str)){ // if openFile return 1, error opening file
printf("error opening file.\n");
return 0;
}
printf("\n%s", str);
free(str);
return 0;
}
and finally, the input/output (input = MEEUMOCSHMSC1T*AGU0A***L2****T*****A):
1 4 36 36
MEEUMOCSHMSC1T*AGU0A***L2****T*****A, 36
MEEUMOCSHMSC1T*A
}
its a cypher, thats why input is so jambled;
that "}" at the end is part of the string I guess, it changes every time AND it dissapears when I substitute str[15] with '\0';
strncpy(str, a, 16);
strncpy(dest, src, length);
length is maximum number of chars copied. So it will not comply more than 16 chars in your case
Related
I am reading a file using a small buffer and printing it. But after every time it after fread and printf some unrecognized characters appear. I do not know why.
I think it has something to do with printf and not fread.
This is the small code:
f = fopen(host_filename2, "r");
char chunks[4];
int size;
do {
memset(chunks,0,sizeof chunks);
size = fread(chunks, 1, sizeof chunks, f);
if (size <= 0) break;
printf("%s", chunks);
} while (size == sizeof chunks);
fclose(f);
printf("%s", chunks); expect chunks[] to be a string. Strings have a null character at the end and fread(chunks, 1, sizeof chunks, f) did not certainly read a '\0' and form a string.
Write what was read (Best)
// printf("%s", chunks);
fwrite(chunks, 1, size, stdout);
Write what was read up to a '\0'
"%.*s" writes a limited amount from a character array, stopping at the size or when a '\0' is detected.
// printf("%s", chunks);
printf("%.*s", size, chunks);
Append your own '\0'
This will perform like printf("%.*s", size, chunks).
char chunks[4 + 1]; // One bigger
int size;
do {
// memset(chunks,0,sizeof chunks - 1); // Not needed
size = fread(chunks, 1, sizeof chunks - 1, f);
if (size <= 0) break;
chunks[size] = '\0';
printf("%s", chunks);
} while (size == sizeof chunks - 1);
Avoid naked magic numbers
Use size_t for array sizing.
#define CHUNK_SIZE 4
char chunks[CHUNK_SIZE];
size_t size;
size_t n = sizeof chunks/sizeof *chunks;
do {
size = fread(chunks, sizeof *chunks, n, f);
if (size <= 0) break;
fwrite(chunks, sizeof *chunks, size, stdout);
} while (size == sizeof chunks);
I was writing a lexical analyzer in which I need to append a char to a string (a char *). For some reason, the code below is resulting in string having a value of "(null)" when I print it to stdout. The function is given below.
void append_char(char *buffer, char c) {
if(buffer == NULL) {
buffer = malloc(sizeof(char));
if(buffer == NULL) {
fprintf(stderr, "COuld not allcocate memory to buffer\n");
}
} else {
buffer = realloc(buffer, sizeof(buffer) + sizeof(char));
}
buffer[sizeof(buffer) - 1] = c;
}
When I run the lines
char *buf = NULL;
append_char(buf, 'a');
append_char(buf, '\0');
printf("buffer: %s\n", buf);
it prints (null) to stdout. How can I fix this?
Pass by value
append_char(char *buffer, char c) does not affect the caller's buf in main(): append_char(buf, 'a');. buf remains NULL. This leads to OP's output.
Insufficient size
Insufficient size for the newly allocated string. No room for the null character.
Wrong size
With char *buffer, sizeof(buffer) is the size of a pointer, not the amount allocated beforehand.
Lost memoery
When buffer = realloc(buffer, sizeof(buffer) + sizeof(char)); fails (realloc() returns NULL) , the original value of buffer is lost. Save the result and test.
Note: OK to call realloc(NULL, ...).
char *append_char(char *buffer, char c) {
size_t old_length = buffer ? strlen(buffer) : 0;
size_t new_length = old_length + 1; // +1 for c
// Size needed for a string is its length + 1
char *new_buffer = realloc(buffer, new_length + 1); // +1 for \0
if (new_buffer == NULL) {
fprintf(stderr, "Could not allocate memory to buffer\n");
free(buffer);
return NULL;
}
new_buffer[old_length] = c;
new_buffer[old_length + 1] = '\0';
return new_buffer;
}
// Usage
buf = append_char(buf, 'a');
There are a number of problems with your program:
buffer is a local variable of append_char(). As soon as this function returns, the buffer variable becomes unusable. You need to pass in the address of buffer to write the address returned by malloc() and realloc() to buffer.
buf is a pointer to a char. sizeof (buf) does not depend on the number of characters in buf.
You do not check the return value from realloc().
You are not allocating space for the NUL terminator.
You do not call free() after you are done.
Here is one way to achieve what you are trying to do (although I am not trying to fix all the problems I mentioned above):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void append_char(char **buffer, char c) {
if(*buffer == NULL) {
if((*buffer = malloc(sizeof(char) + 1)) == NULL) { /* + 1 for the NUL terminator */
fprintf(stderr, "COuld not allcocate memory to buffer\n");
return;
}
(*buffer)[0] = (*buffer)[1] = '\0';
} else {
*buffer = realloc(*buffer, strlen(*buffer) + sizeof(char) + 1 /* for the NUL terminator */);
}
(*buffer)[strlen(*buffer) + 1] = '\0';
(*buffer)[strlen(*buffer)] = c;
}
int main(void)
{
char *buf = NULL;
append_char(&buf, 'a');
append_char(&buf, '\0');
printf("buffer: %s\n", buf);
}
I have a pointer of pointer to store lines I read from a file;
char **lines;
And I'm assigning them like this :
line_no=0;
*(&lines[line_no++])=buffer;
But it crashes why ?
According to my logic the & should give the pointer of zeroth index, then *var=value, that's how to store value in pointer. Isn't it ?
Here is my current complete code :
void read_file(char const *name,int len)
{
int line_no=0;
FILE* file;
int buffer_length = 1024;
char buffer[buffer_length];
file = fopen(name, "r");
while(fgets(buffer, buffer_length, file)) {
printf("---%s", buffer);
++line_no;
if(line_no==0)
{
lines = (char**)malloc(sizeof(*lines) * line_no);
}
else
{
lines = (char**)realloc(lines,sizeof(*lines) * line_no);
}
lines[line_no-1] = (char*)malloc(sizeof(buffer));
lines[line_no-1]=buffer;
printf("-------%s--------\n", *lines[line_no-1]);
}
fclose(file);
}
You have just a pointer, nothing more. You need to allocate memory using malloc().
Actually, you need first to allocate memory for pointers, then allocate memory for strings.
N lines, each M characters long:
char** lines = malloc(sizeof(*lines) * N);
for (int i = 0; i < N; ++i) {
lines[i] = malloc(sizeof(*(lines[i])) * M);
}
You are also taking an address and then immediately dereference it - something like*(&foo) makes little to no sense.
For updated code
Oh, there is so much wrong with that code...
You need to include stdlib.h to use malloc()
lines is undeclared. The char** lines is missing before loop
if in loop checks whether line_no is 0. If it is, then it allocates lines. The problem is, variable line_no is 0 - sizeof(*lines) times 0 is still zero. It allocates no memory.
But! There is ++line_no at the beginning of the loop, therefore line_no is never 0, so malloc() isn't called at all.
lines[line_no-1] = buffer; - it doesn't copy from buffer to lines[line_no-1], it just assigns pointers. To copy strings in C you need to use strcpy()
fgets() adds new line character at the end of buffer - you probably want to remove it: buffer[strcspn(buffer, "\n")] = '\0';
Argument len is never used.
char buffer[buffer_length]; - don't use VLA
It would be better to increment line_no at the end of the loop instead of constantly calculating line_no-1
In C, casting result of malloc() isn't mandatory
There is no check, if opening file failed
You aren't freeing the memory
Considering all of this, I quickly "corrected" it to such state:
void read_file(char const* name)
{
FILE* file = fopen(name, "r");
if (file == NULL) {
return;
}
int buffer_length = 1024;
char buffer[1024];
char** lines = malloc(0);
int line_no = 0;
while (fgets(buffer, buffer_length, file)) {
buffer[strcspn(buffer, "\n")] = '\0';
printf("---%s\n", buffer);
lines = realloc(lines, sizeof (*lines) * (line_no+1));
lines[line_no] = malloc(sizeof (*lines[line_no]) * buffer_length);
strcpy(lines[line_no], buffer);
printf("-------%s--------\n", lines[line_no]);
++line_no;
}
fclose(file);
for (int i = 0; i < line_no; ++i) {
free(lines[i]);
}
free(lines);
}
Ok, you have a couple of errors here:
lines array is not declared
Your allocation is wrong
I don't understand this line, it is pointless to allocate something multiplying it by zero
if( line_no == 0 )
{
lines = (char**)malloc(sizeof(*lines) * line_no);
}
You shouldn't allocate array with just one element and constantly reallocate it. It is a bad practice, time-consuming, and can lead to some bigger problems later.
I recommend you to check this Do I cast the result of malloc? for malloc casting.
You could write something like this:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void read_file(char const *name)
{
int line_no = 0, arr_size = 10;
int buffer_length = 1024;
char buffer[buffer_length];
char **lines;
FILE* file;
lines = malloc(sizeof(char*) * 10);
file = fopen(name, "r");
while(fgets(buffer, buffer_length, file)) {
buffer[strlen(buffer)-1] = '\0';
printf("---%s", buffer);
++line_no;
if(line_no == arr_size)
{
arr_size += 10;
lines = realloc(lines, sizeof(char*) * arr_size);
}
lines[line_no-1] = malloc(sizeof(buffer));
lines[line_no-1] = buffer;
printf("-------%s--------\n", lines[line_no-1]);
}
fclose(file);
}
PS, fgets() also takes the '\n' char at the end, in order to prevent this you can write the following line: buffer[strlen(buffer)-1] = '\0';
I am currently attempting to write a function which scans words in from a dictionary file. It works perfectly in the debugger but when I compile and run it normally, it crashes after five words scanned. Here is my code:
char** readDictionary(FILE *ifp, int size){
int i;
char** dictionary;
char buffer[21];
dictionary = malloc(sizeof(char*) * size);
if(dictionary == NULL){
printf("dictionary allocation ERROR");
return NULL;
}
for(i=0; i<size; i++){
fscanf(ifp, "%s", buffer);
printf("%s\n", buffer); //debugging statement
dictionary[i] = malloc(sizeof(char) * strlen(buffer));
strcpy(dictionary[i], buffer);
}
return dictionary;
}
In the debugger, all words are scanned in properly. When I run without the debugger, I crash after the fifth word.
here is a list of my first few words (again, it crashes after aardvarks):
aahing
aahs
aals
aardvark
aardvarks
aardwolf
aardwolves
I am not sure why this could be happening. Please help.
You are not allocating space for the terminating null byte. Change
dictionary[i] = malloc(sizeof(char) * strlen(buffer));
strcpy(dictionary[i], buffer);
to
size_t length = strlen(buffer);
dictionary[i] = malloc(length + 1);
if (dictionary[i] != NULL)
memcpy(dictionary[i], buffer, length + 1);
Or even better
dictionary[i] = strdup(buffer);
Also, check that fscanf() didn't faile if it didn't it will return 1 in your case.
I need remove punctuation from a given string or a word. Here's my code:
void remove_punc(char* *str)
{
char* ps = *str;
char* nstr;
// should be nstr = malloc(sizeof(char) * (1 + strlen(*str)))
nstr = (char *)malloc(sizeof(char) * strlen(*str));
if (nstr == NULL) {
perror("Memory Error in remove_punc function");
exit(1);
}
// should be memset(nstr, 0, sizeof(char) * (1 + strlen(*str)))
memset(nstr, 0, sizeof(char) * strlen(*str));
while(*ps) {
if(! ispunct(*ps)) {
strncat(nstr, ps, 1);
}
++ps;
}
*str = strdup(nstr);
free(nstr);
}
If my main function is the simple one:
int main(void) {
char* str = "Hello, World!:)";
remove_punc(&str);
printf("%s\n", str);
return 0;
}
It works! The output is Hello World.
Now I want to read in a big file and remove punctuation from the file, then output to another file.
Here's another main function:
int main(void) {
FILE* fp = fopen("book.txt", "r");
FILE* fout = fopen("newbook.txt", "w");
char* str = (char *)malloc(sizeof(char) * 1024);
if (str == NULL) {
perror("Error -- allocating memory");
exit(1);
}
memset(str, 0, sizeof(char) * 1024);
while(1) {
if (fscanf(fp, "%s", str) != 1)
break;
remove_punc(&str);
fprintf(fout, "%s ", str);
}
return 0;
}
When I rerun the program in Visual C++, it reports a
Debug Error! DAMAGE: after Normal Block(#54)0x00550B08,
and the program is aborted.
So, I have to debug the code. Everything works until the statement free(nstr) being executed.
I get confused. Anyone can help me?
You forgot to malloc space for the null terminator. Change
nstr = (char *)malloc(sizeof(char) * strlen(*str));
to
nstr = malloc( strlen(*str) + 1 );
Note that casting malloc is a bad idea, and if you are going to malloc and then memset to zero, you could use calloc instead which does just that.
There is another bug later in your program. The remove_punc function changes str to point to a freshly-allocated buffer that is just big enough for the string with no punctuation. However you then loop up to fscanf(fp, "%s", str). This is no longer reading into a 1024-byte buffer, it is reading into just the buffer size of the previous punctuation-free string.
So unless your file contains lines all in descending order of length (after punctuation removal), you will cause a buffer overflow here. You'll need to rethink your design of this loop. For example perhaps you could have remove_punc leave the input unchanged, and return a pointer to the freshly-allocated string, which you would free after printing.
If you go with this solution, then use %1023s to avoid a buffer overflow with fscanf (unfortunately there's no simple way to take a variable here instead of hardcoding the length). Using a scanf function with a bare "%s" is just as dangerous as gets.
The answer by #MatMcNabb explains the causes of your problems. I'm going to suggest couple of ways you can simplify your code, and make it less susceptible to memory problems.
If performance is not an issue, read the file character by character and discard the puncuation characters.
int main(void)
{
FILE* fp = fopen("book.txt", "r");
FILE* fout = fopen("newbook.txt", "w");
char c;
while ( (c = fgetc(fp)) != EOF )
{
if ( !ispunct(c) )
{
fputc(c, fout);
}
}
fclose(fout);
fclose(fp);
return 0;
}
Minimize the number of calls to malloc and free by passing in the input string as well as the output string to remove_punc.
void remove_punc(char* inStr, char* outStr)
{
char* ps = inStr;
int index = 0;
while(*ps)
{
if(! ispunct(*ps))
{
outStr[index++] = *ps;
}
++ps;
}
outStr[index] = '\0';
}
and change the way you use remove_punc in main.
int main(void)
{
FILE* fp = fopen("book.txt", "r");
FILE* fout = fopen("newbook.txt", "w");
char inStr[1024];
char outStr[1024];
while (fgets(inStr, 1024, fp) != NULL )
{
remove_punc(inStr, outStr);
fprintf(fout, "%s", outStr);
}
fclose(fout);
fclose(fp);
return 0;
}
In your main you have the following
char* str = (char *)malloc(sizeof(char) * 1024);
...
remove_punc(&str);
...
Your remove_punc() function takes the address of str but when you do this in your remove_punc function
...
*str = strdup(nstr);
...
you are not copying the new string to the previously allocated buffer, you are reassigning str to point to the new line sized buffer! This means that when you read lines from the file and the next line to be read is longer than the previous line you will run into trouble.
You should leave the original buffer alone and instead e.g. return the new allocate buffer containing the new string e.g. return nstr and then free that when done with it or better yet just copy the original file byte by byte to the new file and exclude any punctuation. That would be far more effective