Replacing strings in C - c

Okay, guys, I have to write a program that forms a new string based on the given template and the given strings. The template is set as a string where it is necessary to replace all occurrences of the character '%' with a concrete string. If the template contains more characters '%' than the entered strings, characters '%' replaces strings cyclically. If no string is entered, print "ERROR". The number of strings is not known in advance. The end of the entry is indicated by a blank line ("\n").
Also, there are some conditions I need to fulfill, which are:
1) Implement char * readLine () function; which reads one row from the standard input and returns the pointer to that loaded row.
2) Implement char ** readLines (int * n) function; which reads strings that change all occurrences of the character '%' in the template. The function returns an array of pointers to the strings entered as a return value. Also, the function returns the number of values entered via argument n.
3) Implement char * format function (char * format, char ** values, int n); which formats the string format by changing each occurrence of the '%' character to a corresponding string from a string of values of length n.
4) Write a master program that, using previously implemented functions, reads the template and strings from the standard input, forms a new string as described, and prints the result to standard output.
I barely understand what I'm supposed to do so I came here to ask for help. What I did for now is the first task, but on the output I get 1 more blank line than I'm supposed to and I can't find any way to fix it. Any kind of help is welcomed since I'm really stuck here, even explaining what I'm supposed to do in some tasks, or making current code simpler is awesome. Thanks in advance. Here is my code.
EDIT: Added examples.
Input:
% be or not % be.
To
Output:
To be or not To be.
Input:
% and % make purple.
Blue
red
Output:
Blue and red make purple.
#include<stdio.h>
#include<stdlib.h>
char* readLine() {
char* line = malloc(100), * linep = line;
size_t lenmax = 100, len = lenmax;
int c;
if (line == NULL)
return NULL;
for (;;) {
c = fgetc(stdin);
if (c == EOF)
break;
if (--len == 0) {
len = lenmax;
char* linen = realloc(linep, lenmax *= 2);
if (linen == NULL) {
free(linep);
return NULL;
}
line = linen + (line - linep);
linep = linen;
}
if ((*line++ = c) == '\n')
break;
}
*line = '\0';
return linep;
}
int main() {
printf("%s", readLine());
return 0;
}
EDIT: For the 2nd task, I tried to do something similar like in the first, but I couldn't really figure out how to make it so that it works. Here is what I did:
char** readLines(int* n) {
char* line = malloc(100), * linep = line;
size_t lenmax = 100, len = lenmax;
int c, flag = 0;
if (line == NULL && flag > 1) {
char* linen = realloc(linep, lenmax *= 2);
}
else if (line == NULL && flag == 0)
return NULL;
for (;;) {
c = fgetc(stdin);
if (c == EOF || c == '\n')
break;
if (--len == 0) {
len = lenmax;
char* linen = realloc(linep, lenmax *= 2);
size_t diff = line - linep;
if (linen == NULL) {
free(linep);
return NULL;
}
line = linen + diff;
linep = linen;
flag++;
}
*line++ = c;
}
*line = '\0';
char* temp = linep;
return linep;
}

At least this problem
line = linen + (line - linep); is UB, as code cannot use linep after it has been free'd in realloc().
Instead calculate and save the line - linep difference before realloc()
size_t diff = line - linep; // add
char* linen = realloc(linep, lenmax *= 2);
if (linen == NULL) {
...;
}
// line = linen + (line - linep);
line = linen + diff;
OP apparently does not want to retain the '\n'. Change
// if ((*line++ = c) == '\n') break;
if (c == '\n') break;
*line++ = c;
I'd also recommend a right size realloc() step in the end.

If you want to read a line until EOF or '\n', but without the newline in your string, you need to do this:
for (;;) {
c = fgetc(stdin);
if (c == EOF)
break;
// realloc logic comes here
if (c == '\n')
break;
*line++ = c;
}

Related

Problem with removing the "\n" from a string read in with getline()

Hello I am very new to the C programming language and I am writing my first program in C. I want to remove the "\n" from the end of a line read in with getline() and replace it with "\0". I tried it with the code that is in the if-statement, the first expression in my while loop but I am not getting the behaviour I intended.
I tried the solution suggested here Function to remove newline has no effect? but it did not work for my case, and I don't understand why, I assume I am making a mistake with the pointers but I can not figure out what it is. What exactly am I doing wrong and how can I fix it?
void foo(FILE *input, FILE *output) {
char *line = NULL;
size_t length = 0;
ssize_t chars_read;
while ((chars_read= getline(&line, &length, input)) != -1) {
if (line[chars_read-1] == '\n') {
line[chars_read-1] = '\0';
chars_read = chars_read - 1;
}
char *line_rev = malloc(sizeof(char)*chars_read);
bar(line, line_rev, chars_read);
if (strcmp(line, line_rev) == 0) {
...
} else {
...
}
free(line_rev);
}
free(line);
}
Update: Thanks for all the helpful answers! For future visitors: Be careful when working on WSL, new lines might be '\n' or '\r' depending on the OS you are working on. For details check Ted's answer ;).
What you need is simply to put a \0 where the \n is.
It could look like this;
char *line = NULL;
size_t length = 0;
ssize_t chars_read;
// ...
if(chars_read > 0 && line[chars_read-1] == '\n') {
line[chars_read-1] = '\0';
// special care for windows line endings:
if(chars_read > 1 && line[char_read-2] == '\r') line[chars_read-2] = '\0';
}
To replace a potential '\n' in a string with a '\0':
line[strcspn(line, "\n")] = '\0';
To utilizing the prior length chars_read:
if (chars_read > 0 && line[chars_read - 1] == '\n') {
line[--chars_read] = '\0';
}
malloc() is 1 too short for OP's need.
// char *line_rev = malloc(sizeof(char)*chars_read);
char *line_rev = malloc(sizeof(char)*(chars_read + 1));
bar(line, line_rev, chars_read);
if (strcmp(line, line_rev) == 0) {

Reading multiple lines from input in C

I am trying to read multiple lines from console input, but I can't really figure out the way to do it. I made a code that reads only the first line and tried to loop that, but I couldn't figure out the way that works. Any help is welcomed. Thanks in advance. Here is the code:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
char* readLine() {
char* line = malloc(100), * linep = line;
size_t lenmax = 100, len = lenmax;
int c;
if (line == NULL)
return NULL;
for (;;) {
c = fgetc(stdin);
if (c == EOF || c == '\n')
break;
if (--len == 0) {
len = lenmax;
char* linen = realloc(linep, lenmax *= 2);
size_t diff = line - linep;
if (linen == NULL) {
free(linep);
return NULL;
}
line = linen + diff;
linep = linen;
}
*line++ = c;
}
*line = '\0';
return linep;
}
int main() {
for (int i = 0; i < 2; i++) {
printf("%s", readLine());
}
return 0;
}
As you can see, I only managed to loop the function calling twice, which just means I will read only one line twice.
EDIT: I found a way to read code line by line, by doing all the work to the line immediately after its input (saving all I need before the next line is inputted), and tried to stop the while loop while its != '\n', but the loop won't stop, instead it just keeps printing the '\n' character (I keep getting blank lines). Any help on how to fix this?
// Same stuff here
int main() {
char* temp = readLine();
while (*temp != '\n') {
char* temp = readLine();
printf("%s\n", temp);
}
return 0;
}

Use scanf to read lines or break on special character

Is it possible to read lines of text with scanf() - excluding \n and break on special(chosen) character, but include that character
This is my current expression: while(scanf("%49[^:\n]%*c", x)==1)
but this one excludes :.
Is it possible to break reading on : but read that character too?
Ok I am using Johannes-Schaub-litb's code.
char * getline(char cp) {
char * line = malloc(100), * linep = line;
size_t lenmax = 100, len = lenmax;
int c;
if(line == NULL)
return NULL;
for(;;) {
c = fgetc(stdin);
if(c == EOF)
break;
if(--len == 0) {
len = lenmax;
intptr_t diff = line - linep;
char * linen = realloc(linep, lenmax *= 2);
if(linen == NULL) {
free(linep);
return NULL;
}
line = linen + diff;
linep = linen;
}
if((*line++ = c) == cp)
break;
}
*line = '\0';
return linep;
}
Still I use this code ...and it works fine.
The code will be modified a bit more later.
Is it possible to read lines of text with scanf() - excluding \n and break on special(chosen) character, but include that character(?)
Yes. But scanf() is notorious for being used wrong and difficult to use right. Certainly the scanf() approach will work for most user input. Only a rare implementation will completely meet OP's goal without missing corner cases. IMO, it is not worth it.
Alternatively, let us try the direct approach, repeatedly use fgetc(). Some untested code:
char *luvatar_readline(char *destination, size_t size, char special) {
assert(size > 0);
char *p = destitution;
int ch;
while (((ch = fgetc(stdin)) != EOF) && (ch != '\n')) {
if (size > 1) {
size--;
*p++ = ch;
} else {
// Ignore extra input or
// TBD what should be done if no room left
}
if (ch == (unsigned char) special) {
break;
}
}
*p = '\0';
if (ch == EOF) {
// If rare input error
if (ferror(stdin)) {
return NULL;
}
// If nothing read and end-of-file
if ((p == destination) && feof(stdin)) {
return NULL;
}
}
return destination;
}
Sample usage
char buffer[50];
while (luvatar_readline(buffer, sizeof buffer_t, ':')) {
puts(buffer);
}
Corner cases TBD: Unclear what OP wants if special is '\n' or '\0'.
OP's while(scanf("%49[^:\n]%*c", x)==1) has many problems.
Does not cope with input the begins with : or '\n', leaving x unset.
Does not know if the character after the non-:, non-'\n' input was a :, '\n', EOF.
Does not consume extra input past 49.
Uses a fixed spacial character ':', rather than a general one.
I think that you want to do that:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char *line = NULL;
size_t len = 0;
ssize_t read;
while ((read = getline(&line, &len, stdin)) != -1) {
if (read > 0 && line[read - 1] == '\n') {
if (read > 1 && line[read - 2] == '\r') {
line[read - 2] = '\0'; // we can remove the carriage return
}
else {
line[read - 1] = '\0'; // we can remove the new line
}
}
char const *delim = ":";
printf("parsing line :\n");
char *token = strtok(line, delim);
while (token != NULL) {
printf("token: %s\n", token);
token = strtok(NULL, delim);
}
}
free(line);
}
I have done this in a little different way. Maybe this can crash on Windows.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char *input_str;
/**
* Dynamic memory allocation.
* Might crash on windows.
*/
int status = scanf("%m[^.]", &input_str);
/**
* If the first character is the
* terminating character then scanf scans nothing
* and returns 0.
*/
if (status > 0) {
/**
* Calculate the length of the string.
*/
size_t len = strlen(input_str);
/**
* While allocating memory provide
* two extra cell. One for the character
* you want to include.
* One for the NULL character.
*/
char *new_str = (char*) calloc (len + 2, sizeof(char));
/**
* Check for memory allocation.
*/
if(new_str == NULL) {
printf("Memory Allocation failed\n");
exit(1);
}
/**
* Copy the string.
*/
strcpy(new_str, input_str, len);
/**
* get your desired terminating character
* from buffer
*/
new_str[len++] = getc(stdin);
/**
* Append the NULL character
*/
new_str[len++] = '\0';
/**
* eat other characters
* from buffer.
*/
while(getc(stdin) != '\n');
/**
* Free the memory used in
* dynamic memory allocation
* in scanf. Which is a must
* according to the scanf man page.
*/
free(input_str);
} else {
char new_str[2] = ".\0";
/**
* eat other characters
* from buffer.
*/
while(getc(stdin) != '\n');
}
}
I have used dot as a terminating character.

Load File Into Char* Array

Feel silly asking this question, since this should be easy, but I can't figure out whats wrong.
void loadIniIntoMemory() {
FILE *fp ;
fp = fopen (iniFile, "r");
int ch;
int final_line_num = 0;
int char_index;
char* current_line = (char*) malloc(sizeof(char) * MAX_INI_LINE_LENGTH);
while((ch = fgetc(fp)) != EOF) {
if(ch == 10) {
// new line
*(current_line + char_index) = '\0';
char_index = 0;
iniFileData[final_line_num] = current_line;
final_line_num++;
} else {
// regular char
*(current_line + char_index) = ch; // CAN'T DO THIS, CRASH
char_index++;
if(ch == 13) {
// carriage return
continue;
}
}
}
}
Been a little while since I did C, it crashes at this line : *(current_line + char_index) = ch;
Thanks for any help.
--EDIT--
Also, no one noticed, that this code doesn't save the last line. Here is the full, correct, working code which saves a file into an array of pointers.
void loadIniIntoMemory() {
FILE *fp ;
fp = fopen (iniFile, "r");
int ch;
final_line_num = 0;
int char_index = 0;
char* current_line = (char*) malloc(sizeof(char) * MAX_INI_LINE_LENGTH);
while((ch = fgetc(fp)) != EOF) {
if(ch == '\n') {
// new line
*(current_line + char_index) = '\0';
char_index = 0;
iniFileData[final_line_num] = current_line;
final_line_num++;
current_line = (char*) malloc(sizeof(char) * MAX_INI_LINE_LENGTH);
} else if(ch != '\r') {
// regular char
*(current_line + char_index) = ch;
char_index++;
}
}
iniFileData[final_line_num] = current_line;
fclose(fp);
}
For starters, you don't initialize char_index, meaning it will likely have garbage in it. If you don't initialize it, your program will add some unknown number to the current_line pointer.
int char_index = 0; /* initialize to 0 */
Secondly, a bit more "natural" syntax would be:
current_line[char_index] = ...
Thirdly, you can test the characters without using their integer equivalents:
if (ch == '\n') {
/* this is the same as "ch == 10" */
Fourth, you should close the open file prior to leaving the routine:
fclose(fp);
Finally, I'm not sure what the ch == 13 ('\r') and continue is meant to handle, since the continue is effectively a no-op, but you probably don't want to copy it into the data:
if (ch != '\r') {
current_line[char_index] = ch;
char_index++;
/* or on one line: current_line[char_index++] = ch; */
}
As an aside, a powerful feature of C (and many other languages) is the switch statement:
/* substitutes your if...elseif...else */
switch (ch) {
case '\n':
current_line[char_index] = '\0';
char_index = 0;
iniFileData[final_line_num++] = current_line;
break; /* <-- very important, C allows switch cases to fall thru */
case '\r':
/* do nothing */
break;
default:
/* any character that is not a newline or linefeed */
current_line[char_index++] = ch;
break;
}
You didn't initialize char_index. I suppose you want to initialize it to 0. In C, uninitialized variable will contain garbage. Most likely your char_index equals some very large number.
Besides what others have already pointed out, you will also have to move the line buffer allocation call into the loop and immediately after the final_line_num++; statement. Otherwise, each new line you read will be overwriting the previous line.
And some operating systems, like most POSIX compliant ones, in particular Linux, gives you the ability to map a file segment (possibly the entire file) into virtual memory. On Linux, you could consider using the mmap system call

Reading a text file into 2 separate arrays of characters (in C)

For a class I have to write a program to read in a text file in the format of:
T A E D Q Q
Z H P N I U
C K E W D I
V U X O F C
B P I R G K
N R T B R B
EXIT
THE
QUICK
BROWN
FOX
I'm trying to get the characters into an array of chars, each line being its own array.
I'm able to read from the file okay, and this is the code I use to parse the file:
char** getLinesInFile(char *filepath)
{
FILE *file;
const char mode = 'r';
file = fopen(filepath, &mode);
char **textInFile;
/* Reads the number of lines in the file. */
int numLines = 0;
char charRead = fgetc(file);
while (charRead != EOF)
{
if(charRead == '\n' || charRead == '\r')
{
numLines++;
}
charRead = fgetc(file);
}
fseek(file, 0L, SEEK_SET);
textInFile = (char**) malloc(sizeof(char*) * numLines);
/* Sizes the array of text lines. */
int line = 0;
int numChars = 1;
charRead = fgetc(file);
while (charRead != EOF)
{
if(charRead == '\n' || charRead == '\r')
{
textInFile[line] = (char*) malloc(sizeof(char) * numChars);
line++;
numChars = 0;
}
else if(charRead != ' ')
{
numChars++;
}
charRead = fgetc(file);
}
/* Fill the array with the characters */
fseek(file, 0L, SEEK_SET);
charRead = fgetc(file);
line = 0;
int charNumber = 0;
while (charRead != EOF)
{
if(charRead == '\n' || charRead == '\r')
{
line++;
charNumber = 0;
}
else if(charRead != ' ')
{
textInFile[line][charNumber] = charRead;
charNumber++;
}
charRead = fgetc(file);
}
return textInFile;
}
This is a run of my program:
Welcome to Word search!
Enter the file you would like us to parse:testFile.txt
TAEDQQ!ZHPNIU!CKEWDI!VUXOFC!BPIRGK!NRTBRB!EXIT!THE!QUICK!BROWN!FOX
Segmentation fault
What's going on? A), why are the exclamation marks there, and B) why do I get a seg fault at the end? The last thing I do in the main is iterate through the array/pointers.
1) In the first part of your program, you are miscounting the number of lines in the file. The actual number of lines in the file is 11, but your program gets 10. You need to start counting from 1, as there will always be at least one line in the file. So change
int numLines = 0;
to
int numLines = 1;
2) In the second part of the program you are miscounting the number of characters on each line. You need to keep your counter initializations the same. At the start of the segment you initialize numChars to 1. In that case you need to reset your counter to 1 after each iteration, so change:
numChars = 0;
to
numChars = 1;
This should provide enough space for all the non-space characters and for the ending NULL terminator. Keep in mind that in C char* strings are always NULL terminated.
3) Your program also does not account for differences in line termination, but under my test environment that is not a problem -- fgetc returns only one character for the line terminator, even though the file is saved with \r\n terminators.
4) In the second part of your program, you are also not allocating memory for the very last line. This causes your segfault in the third part of your program when you try to access the unallocated space.
Note how your code only saves lines if they end in \r or \n. Guess what, EOF which technically is the line ending for the last line does not qualify. So your second loop does not save the last line into the array.
To fix this, add this after the second part:
textInFile[line] = (char*) malloc(sizeof(char) * numChars);
4) In your program output you are seeing those weird exclamation points because you are not NULL terminating your strings. So you need to add the line marked as NULL termination below:
if(charRead == '\n' || charRead == '\r')
{
textInFile[line][charNumber] = 0; // NULL termination
line++;
charNumber = 0;
}
5) Because you are checking for EOF, you have the same problem in your third loop, so you must add this before the return
textInFile[line][charNumber] = 0; // NULL termination
6) I am also getting some headaches because of the whole program structure. You read the same file character by character 3 times! This is extremely slow and inefficient.
Fixed code follows below:
char** getLinesInFile(char *filepath)
{
FILE *file;
const char mode = 'r';
file = fopen(filepath, &mode);
char **textInFile;
/* Reads the number of lines in the file. */
int numLines = 1;
char charRead = fgetc(file);
while (charRead != EOF)
{
if(charRead == '\n' || charRead == '\r')
{
numLines++;
}
charRead = fgetc(file);
}
fseek(file, 0L, SEEK_SET);
textInFile = (char**) malloc(sizeof(char*) * numLines);
/* Sizes the array of text lines. */
int line = 0;
int numChars = 1;
charRead = fgetc(file);
while (charRead != EOF)
{
if(charRead == '\n' || charRead == '\r')
{
textInFile[line] = (char*) malloc(sizeof(char) * numChars);
line++;
numChars = 1;
}
else if(charRead != ' ')
{
numChars++;
}
charRead = fgetc(file);
}
textInFile[line] = (char*) malloc(sizeof(char) * numChars);
/* Fill the array with the characters */
fseek(file, 0L, SEEK_SET);
charRead = fgetc(file);
line = 0;
int charNumber = 0;
while (charRead != EOF)
{
if(charRead == '\n' || charRead == '\r')
{
textInFile[line][charNumber] = 0; // NULL termination
line++;
charNumber = 0;
}
else if(charRead != ' ')
{
textInFile[line][charNumber] = charRead;
charNumber++;
}
charRead = fgetc(file);
}
textInFile[line][charNumber] = 0; // NULL termination
return textInFile;
}
You aren't null terminating your arrays. This probably explains both problems. Be sure to allocate an extra character for the null terminator.
Do This:
if(charRead == '\n')
{
textInFile[line] = (char*) malloc(sizeof(char) * (numChars+1));
line++;
numChars = 0;
}
Then:
if(charRead == '\n')
{
textInFile[line][charNumber]='\0';
line++;
charNumber = 0;
}
Also you are reading the file 3 times! This thread has some good explanation on how to read a file efficiently.

Resources