C Concatenate string in while loop [duplicate] - c

This question already has answers here:
C Concatenate string in while loop
(2 answers)
Closed 6 years ago.
I cant seem to use the following function to concatenate a string in the while loop. Any idea what Im doing wrong?
void do_file(FILE *in, FILE *out, OPTIONS *options)
{
char ch;
int loop = 0;
int sz1,sz2,sz3;
int seeker = offsetof(struct myStruct, contents.datas);
//find total length of file
fseek(in, 0L, SEEK_END);
sz1 = ftell(in);
//find length to struct beginning and minus that from total length
fseek(in, seeker, SEEK_SET);
sz2 = sz1 - ftell(in);
sz3 = (sz2 + 1) * 2;//Allocate enough size for 2x array length
char buffer[sz3];//set size of buffer to be copied to
char msg[sz3];//set size of msg
buffer[0] = '\0';
while (loop < sz2)
{
if (loop == sz2)
{
break;
}
fread(&ch, 1, 1, in);
//sprintf(msg, "%02X", (ch & 0x00FF));
strcat(buffer, msg);
++loop;
}
printf("%s\n", buffer);
}

Your only strcat appends the char msg[sz3]; which is never filled.
EDIT
The simplest fix would be to add
msg[1] = '\0';
after
char msg[sz3];//set size of msg
and to replace
fread(&ch, 1, 1, in);
with
fread(&msg[0], 1, 1, in);

Related

unexpected undefined characters fread

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);

Printing all phrases in a file with C program

I need to print all phrases from a file (phrases can end in '.', '?' or '!')
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char* read_file(char *name) {
FILE *file;
char *text;
long num_bytes;
file = fopen(name, "r");
if(!file) {
printf("File could not be opened!");
exit(EXIT_FAILURE);
}
fseek(file, 0, SEEK_END);
num_bytes = ftell(file);
fseek(file, 0, SEEK_SET);
text = (char*) malloc(num_bytes * sizeof(char));
fread(text, 1, num_bytes, file);
fclose(file);
return text;
}
I have this piece of code that kind of works but if my file as the following text: "My name is Maria. I'm 19." the second phrase is printed with a ' ' in the beggining.
Can someone please help finding a way to ignore those spaces? Thank you
To start, you have several problems that will invoke Undefined Behaviour. In
char *line = (char*) malloc(sizeof(text));
sizeof (text) is the size of a pointer (char *), not the length of the buffer it points to.
sizeof (char *) depends on your system, but is very likely to be 8 (go ahead and test this: printf("%zu\n", sizeof (char *));, if you are curious), which means line can hold a string of length 7 (plus the null-terminating byte).
Long sentences will easily overflow this buffer, leading to UB.
(Aside: do not cast the return of malloc in C.)
Additionally, strlen(text) may not work properly as text may not include the null-terminating byte ('\0'). fread works with raw bytes, and does not understand the concept of a null-terminated string - files do not have to be null-terminated, and fread will not null-terminate buffers for you.
You should allocate one additional byte to in the read_file function
text = malloc(num_bytes + 1);
text[num_bytes] = 0;
and place the null-terminating byte there.
(Aside: sizeof (char) is guaranteed to be 1.)
Note that ftell to determine the length of a file should not be relied upon.
isspace from <ctype.h> can be used to determine if the current character is whitespace. Its argument should be cast to unsigned char. Note this will include characters such as '\t' and '\n'. Use simple comparison if you only care about spaces (text[i + 1] == ' ').
A loop can be used to consume the trailing whitespace after matching a delimiter.
Make sure to null-terminate line before printing it, as %s expects a string.
Use %u to print an unsigned int.
Do not forget to free your dynamically allocated memory when you are done with it. Additionally, heavily consider checking any library function that can fail has not done so.
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void pdie(const char *msg) {
perror(msg);
exit(EXIT_FAILURE);
}
char *read_file(char *name) {
FILE *file = fopen(name, "r");
if (!file)
pdie(name);
fseek(file, 0, SEEK_END);
long num_bytes = ftell(file);
if (-1 == num_bytes)
pdie(name);
fseek(file, 0, SEEK_SET);
char *text = malloc(num_bytes + 1);
if (!text)
pdie("malloc");
if (-1 == num_bytes)
pdie(name);
text[num_bytes] = 0;
if (fread(text, 1, num_bytes, file) != num_bytes)
pdie(name);
fclose(file);
return text;
}
int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "usage: %s TEXT_FILE\n", argv[0]);
return EXIT_FAILURE;
}
char *text = read_file(argv[1]);
unsigned int count = 0;
size_t length = strlen(text);
size_t index = 0;
char *line = malloc(length + 1);
if (!line)
pdie("malloc");
for (size_t i = 0; i < length; i++) {
line[index++] = text[i];
if (text[i] == '.' || text[i] == '?' || text[i] == '!') {
line[index] = '\0';
index = 0;
printf("[%u] <<%s>>\n", ++count, line);
while (isspace((unsigned char) text[i + 1]))
i++;
}
}
free(text);
free(line);
return EXIT_SUCCESS;
}
Input file:
My name is Maria. I'm 19. Hello world! How are you?
stdout:
[1] <<My name is Maria.>>
[2] <<I'm 19.>>
[3] <<Hello world!>>
[4] <<How are you?>>
You can test for a whitespace character by comparing the char in question to ' '.
if(text[i] == ' ')
// text[i] is whitespace
One possible solution, advance to the next non-whitespace character when you find the end of the sentence. You also need to make sure you've mallocd enough memory for the current phrase:
#include <ctype.h> // for isspace
...
size_t textLength = strlen(text);
// malloc based on the text length here, plus 1 for the NUL terminator.
// sizeof(text) gives you the size of the pointer, not the size of the
// memory block it points to.
char *line = malloc(textLength+1);
for(size_t i = 0; i < textLength; i++) {
line[index] = text[i];
index++;
if(text[i] == '.' || text[i] == '?' || text[i] == '!') {
count++;
printf("[%d] %s\n", count, line);
memset(line, 0, index + 1);
index = 0;
// advance to the next non-whitespace char
do
{
// advance to the next char (we know the current char is not a space)
i++;
// keep advancing i while the next char is in range of the
// text and the next char is a space.
}while (i+1 < textLength && isspace(text[i+1]) != 0);
}
}
Output:
[1] My name is Maria.
[2] I'm 19.
Demonstration
There's also no need to cast the return value of malloc

strncpy only copying 16 bytes from one string to another

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

How to allocate memory to an char array, how to use pointers for that? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
As described above I happen to have a problem with allocating memory for my char array. The point of allocating memory is that I want to read a .txt file and save all of it in an char array to later work with it. (Looking for the biggest duplicate string)
I' ve looked for over 2 hours now on the internet before posting but I just can't get it working it seems like I am missing something or just don't completely understand how pointers work which I really don't.
The comments are german like some of the variables I hope noone minds that.
Here is the code:
# include <stdio.h>
# include <stdlib.h>
# include <math.h>
# include <string.h>
# include <ctype.h>
# define N 99
# define K 255
main() {
FILE *Test;
long len = 1;
int i = 0;
int k;
// char String[N]; //[100][255] = {" "};
// char* pString;
// pString = String;
Test = fopen("Moby_Dick.txt", "r"); // Datei öffnen, r = lesen | (nicht vergessen DATEINAMEN anzupassen)
if (fseek(Test, 0L, SEEK_END) != 0) { //jump ans Ende
printf("\nFEHLER in SEEK_END");
fclose(Test);
return 3;
}
if ((len = ftell(Test)) < 0L) {
printf("\nERROR in ftell");
fclose(Test);
return 3;
}
if (fseek(Test, 0L, SEEK_SET) != 0) { // jump an den Anfang
printf("\nERROR in SEEK_SET");
fclose(Test);
return 3;
}
char *String = (char*)malloc(sizeof(char)*len + 12); //(char*)
Test = fopen("Eingabe.txt", "r");
do{
fgets(String[i], 500, Test); // jede Zeile wird in einem Feld gespeichert
i++; // zählt die Zeilen hoch
}while(!feof(Test)); // lese bis Dateiende
fclose(Test); // Datei schliessen
for (k = 0; k < 20; k++) {
printf("%s\n----------------------------\n", String[k]);
}
printf("len = %d\n", len);
return 0;
}
Thanks for any help I can get. I would love an explanation instead of just passing the code over.
After allocating the array, you can read the entire file in a single function call:
FILE* file = fopen("Moby_Dick.txt","rt");
fseek(file,0,SEEK_END);
int size = ftell(file);
fseek(file,0,SEEK_SET);
char* string = (char*)malloc(size+1);
fread(string,size,1,file);
fclose(file);
string[size] = 0;
printf("%s",string);
free(string);
Some ideas
Using ftell()/fseek() is not a portable nor great way of finding the length of a file. To fix would require significant work here. For now, let it be, but know better methods exist.
The cast of malloc() results is not needed. The extra 12 bytes is arbitrary and not needed. sizeof(char) is always 1. If anyhting use sizeof(*String).
// char *String = (char*) malloc(sizeof(char) * len + 12); //(char*)
char *String = malloc(len * sizeof(*String));
// or
char *String = malloc(len);
The loop do{ fgets(String[i] ... while(!feof(Test)); has many problems. Better to do something suggested by #barak manos
// fgets(String[i], 500, Test);
if (1 != fread(string,size,1,file)) {
Handle_Readfailure();
}
Match your format
// printf("len = %d\n", len);
printf("len = %ld\n", len);
But having addressed the above minor issues, the approach could improve. Your goal:
read a .txt file and save all of it .. to later work with it. (Looking for the biggest duplicate string)
To accomplish this suggest:
Read each line of text.
Walk through list looking for "biggest duplicate".
The below is not tested, and is missing some error handling. There is room for performance improvements. But it does not depend on Test being a file, it could be a stream like stdin. Need to add free()-ing the buffers. It does assume a maximum line length of 1024;
// Read lines
size_t LineCount = 0;
size_t LineSize = 0;
char **Line = NULL;
char buffer[1024];
while (fgets(buffer, sizeof buffer, Test) != NULL) {
if (LineCount >= LineSize) {
LineSize = LineSize*2 + 1;
Line = realloc(Line, LineSize * sizeof(*Line)); // TBD error handling
}
size_t len = strlen(buffer);
if (len > 0 && buffer[len - 1] == '\n') buffer[--len] = '\0';
len++;
Line[LineCount] = malloc(len);
memcpy(Line[LineCount], buffer, len);
LineCount++;
}
fclose(Test);
// search lines
size_t Longest = 0;
size_t Index = 0;
for (size_t i=0; i<LineCount; i++) {
size_t leni = strlen(Line[i]) + 1;
if (leni <= Longest) continue; // line too short to beat the best.
for (size_t j=0; j<i; j++) {
if (strcmp(Line[i], Line[j]) == 0) { // duplicate found
Longest = leni;
Index = i;
break;// skip checking Line[i] against Line[j+1] to Line[i-1]
}
}
}
if (Longest > 0) {
printf("Longest duplicate line is \"%s\".\n", Line[Index]);
}
else {
printf("No duplicates found.\n");
}

Why is my array of pointers getting overwritten after dynamic allocation?

I'm working on a little C program for a class that reads the lines in from a file and then sorts them using qsort. Long story short, I am dynamically allocating memory for every line of a file, stored as a char*, in an array of char*. The reading in and storing ostensibly works fine based upon the output (see below), but when I print out the lines, they are all duplicates of the last line in the file. Can anyone point out my (most likely painfully obvious) error?
Here is the relevant code to the problem I'm currently running into:
char* trim_white_space(char* str);
char* get_line(FILE* infile, char temp[]);
int main(int argc, char* argv[]) {
FILE* infile;
char* input_file = argv[1];
int cnt = 0;
char temp[MAX_LINE_LENGTH]; //to hold each line as it gets read in
char* tempPointer = temp;
if (argc < 2) {
printf("No input file provided");
return EXIT_FAILURE;
}
//determine the number of lines in the file
infile = fopen(input_file, "r");
int num_lines_in_file = num_lines(infile);
fclose(infile);
//allocate pointers for each line
char** lines = (char**) malloc(num_lines_in_file * sizeof(char*));
//temporarily store each line, and then dynamically allocate exact memory for them
infile = fopen(input_file, "r");
for (cnt = 0; cnt != num_lines_in_file; cnt++) {
tempPointer = get_line(infile, temp);
lines[cnt] = (char*) malloc(strlen(tempPointer) + 1);
lines[cnt] = trim_white_space(tempPointer);
printf("%d: %s\n", cnt, lines[cnt]);
}
fclose(infile);
//print the unsorted lines (for debugging purposes)
printf("Unsorted list:\n");
for (cnt = 0; cnt != num_lines_in_file; cnt++) {
printf("%s\n", lines[cnt]);
}
char* get_line(FILE* infile, char temp[]) {
fgets(temp, MAX_LINE_LENGTH-1, infile);
char* pntr = temp;
return pntr;
}
char *trimwhitespace(char *str)
{
char *end;
// Trim leading space
while(isspace(*str)) str++;
if(*str == 0) // All spaces?
return str;
// Trim trailing space
end = str + strlen(str) - 1;
while(end > str && isspace(*end)) end--;
// Write new null terminator
*(end+1) = 0;
return str;
}
I have this sample input file 5-1input.dat:
Hi guys
x2 My name is
Slim Shady
For real
And here's the output I'm getting:
user#user-VirtualBox ~/Desktop/Low-level/HW5 $ ./homework5-1 5-1input.dat
0: Hi guys
1: x2 My name is
2: Slim Shady
3: For real
Unsorted list:
For real
For real
For real
For real
As in the comments, you should change your loop to:
for (cnt = 0; cnt != num_lines_in_file; cnt++) {
tempPointer = get_line(infile, temp);
lines[cnt] = (char*) malloc(strlen(tempPointer) + 1);
strncpy(lines[cnt], trim_white_space(tempPointer), strlen(tempPointer)+1);
printf("%d: %s\n", cnt, lines[cnt]);
}
The size in strncpy is based on the size of malloc you've used.
Of course you can optimize this code, e.g. to count strlen only once, etc.

Resources