getline doesn't store the string in the variable - c

I wrote a function that opens a file which name is given by the user:
#include <stdio.h>
#include <stdlib.h>
void getfile(FILE** pfile)
{
void getrep(char*,char,char);
void clear(void);
char rep;
char* nfile=NULL;
printf("Name of the file: ");
clear();
nfile=NULL;
getline(&nfile,NULL,stdin);
printf("The name of the file is: %s\n",nfile);
*pfile=fopen(nfile,"r");
while(!*pfile)
{
printf("Can't open the file. Want to retry <Y/N> ? ");
getrep(&rep,'Y','N');
if(rep=='Y')
{
system("clear");
free(nfile);
nfile=NULL;
printf("Name of the file: ");
clear();
getline(&nfile,NULL,stdin);
printf("The name of the file is: %s\n",nfile);
*pfile=fopen(nfile,"r");
}
else
exit(-1);
}
free(nfile);
}
The getrep function simply ensures that the user gives Y or N or y or n as an answer. Here's the clear function:
#include <stdio.h>
void clear(void)
{
char c;
while((c=getchar())!=EOF && c!='\n');
}
Here's what I get when I run the program:
Name of the file: Data.dat
The name of the file is: (null)
Can't open the file. Want to retry ?
When I used the debugger gdb and printed the value of nfile after entering the file's name, it remains 0x0, i.e, NULL. (You might have noticed that I allocated no memory for nfile, but I initialized this variable to NULL so that getline will do it for me. I'm using getline instead of gets because it seems better and, after all, ubuntu 16.04 hates gets)
I believe the reason why this is happening is that when the user is asked to enter the name, it's due to the getchar() in the clear function. Thus the name entered by the user is erased, and the nfile receives nothing in getline. I also tried using this clear function instead:
#include <stdio.h>
void clear2(void)
{
char c;
while((c=getchar())!='\n');
}
Unfortunately, I get the same result. I used fflush(stdin); instead of clear(); but this time the program skips getline not letting the user enter anything. I also removed the space that comes after file: in printf("Name of the file: "); but nothing changes.
Could you please help me? Thanks in advance!

From the getline manual page`:
If *lineptr is set to NULL and *n is set 0 before the call, then getline() will allocate a buffer for storing the line
Since you pass a NULL pointers as the n argument, the call will not allocate a buffer for you. You need to explicitly pass a pointer to a variable of size_t that has been initialized to zero:
char *nfile = NULL;
size_t n = 0;
getline(&nfile,&n,stdin);

The issue is in the getline call.
The second parameter passed in is NULL which is incorrect.
Rather it should be like this:
size_t n = 0;
getline(&nfile,&n,stdin);
As per the man page for getline, states:
ssize_t getline(char **lineptr, size_t *n, FILE *stream);
If *lineptr is set to NULL and *n is set 0 before the call, then
getline() will allocate a buffer for storing the line. This buffer
should be freed by the user program even if getline() failed.

Related

Trying to fscanf with 2d array that uses calloc()

I'm trying to read text from a file and print it in the terminal while using dynamic memory(?), but as soon as I use calloc the code crashes. I'm new to C so I don't know what's wrong
#include <stdio.h>
#include <stdlib.h>
void filecheck(FILE*);
int main(void){
int i=0;
char** text=(char**)calloc(50,sizeof(char*));
for(i=0;i<50;i++) text[i]=(char*)calloc(50,sizeof(char));
FILE *file = fopen("F1.txt","r");
filecheck(file);
while(fscanf(file,"%s", text[i])!=EOF){
printf("%s\n",text[i]);
i++;
}
free(text);
return 0;
}
void filecheck(FILE*file){
if(file==NULL){
printf("Problem");
exit(0);
}
}
The problem is that you don't set i to 0 before you use it in the 2nd while loop. This cause the segmentation fault when you access text out of bounds. I addressed that issue below by using the same type of for loop that you used to initialize the array in the first place.
Bonus items:
Reformatted code for readability (to me) with spaces and moved * next to variable instead of next to type.
Introduced a couple of defines to replace your magic 50 numbers.
Moved filecheck() before main() so you don't need the declaration.
filecheck() now return a status code. This allows main() to free memory on failure which was technically a memory leak (even if the OS does this for you).
Check return value of calloc.
Use a status variable to hold exit code. This allows for clean-up to be shared in both normal and failure case.
Used variable instead of type as argument to sizeof.
Declare the variable as part of each for loop instead of reusing a variable. Reuse is not wrong, btw, but I think it's a good practice even if you use the same variable name.
fgets() instead of fscanf(). fscanf() is subject to buffer overflow when reading strings. Note: fscanf() reads a sequence of non-white-space characters, while fgets() read a line including the '\n'. Removed the the '\n' in the subsequent printf().
Only read at most ARR_LEN strings.
fclose() file descriptor (even if OS would do this for you).
Free the memory you allocate for text[i]. It is technically a memory leak if you don't (even if the OS frees it for you).
#include <stdio.h>
#include <stdlib.h>
#define ARR_LEN 50
#define STR_LEN 50
int filecheck(FILE *file) {
if(!file) {
printf("Problem");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
int main(void) {
int status = EXIT_FAILURE;
char **text = calloc(ARR_LEN, sizeof(*text));
if(!text)
goto out;
for(int i=0; i < ARR_LEN; i++) {
text[i] = calloc(STR_LEN, sizeof(**text));
if(!text[i])
goto out;
}
FILE *file = fopen("F1.txt","r");
if(filecheck(file) != EXIT_SUCCESS)
goto out;
for(int i=0; (i < ARR_LEN) && fgets(text[i], STR_SIZE, file); i++)
printf("%s",text[i]);
status = EXIT_SUCCESS;
out:
if(file) fclose(file);
for(int i=0; i<ARR_LEN; i++)
free(text[i]);
free(text);
return status;
}
Other error (except i not set to 0) is that the function fscanf returns a number of scanned arguments. So you should use:
while (fscanf(file,"%s", text[i])!=1) {
...
}
Moreover, individual text[i] are never freed and leak.

Reading char* with gets() makes "Core Dumped" error (C language)

I'm trying to read a user input with a non-fixed char array, but it just soft crashes (no crash window) when I input something on the keyboard. When I run it on an online C compiler, that's when its says Segmentation fault (core dumped).
My code:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int validerNAS(char* userInput);
int main() {
int valid = 0;
char* userInput;
do {
printf("Enter 9 characters: ");
gets(userInput);
valid = validerNAS(userInput);
} while (!valid);
return 0;
}
int validerNAS(char* userInput) {
if ((strlen(userInput) != 9)) {
printf("Error! You must enter 9 characters\n\n");
return 0;
}
return 0;
}
Here
char* userInput;
userInput doesn't have any valid memory so that you can put some data into it like
gets(userInput); /* this causes seg.fault because till now userInput doesn't have any valid memory */
So to overcome this problem either use character array like
char userInput[100] = {0};
or create dynamic array and then scan the data into dynamically allocated memory.
Also don't use gets(), use fgets() instead as stated in here
For e.g
char* userInput = malloc(SOME_SIZE); /* define SOME_SIZE, creating dynamic array equal to SOME_SIZE */
fgets(userInput ,SOME_SIZE, stdin) ; /* scan the data from user & store into dynamically created buffer */
Side note, from the manual page from fgets
If a newline is read, it is stored into the buffer. A
terminating null byte (aq\0aq) is stored after the last character in
the buffer.
So remove the trailing new line character by calling strcspn(). For e.g
userInput[strcspn(userInput, "\n")] = 0;
And once usage of dynamic array userInput is done, don'tr forget to free the dynamically allocated memory by calling free() to avoid memory leakage. For e.g
free(userInput);

Reading an unknown length line from stdin in c with fgets

I am trying to read an unknown length line from stdin using the C language.
I have seen this when looking on the net:
char** str;
gets(&str);
But it seems to cause me some problems and I don't really understand how it is possible to do it this way.
Can you explain me why this example works/doesn't work
and what will be the correct way to implement it (with malloc?)
You don't want a pointer to pointer to char, use an array of chars
char str[128];
or a pointer to char
char *str;
if you choose a pointer you need to reserve space using malloc
str = malloc(128);
Then you can use fgets
fgets(str, 128, stdin);
and remove the trailling newline
char *ptr = strchr(str, '\n');
if (ptr != NULL) *ptr = '\0';
To read an arbitrary long line, you can use getline (a function added to the GNU version of libc):
#define _GNU_SOURCE
#include <stdio.h>
char *foo(FILE * f)
{
int n = 0, result;
char *buf;
result = getline(&buf, &n, f);
if (result < 0) return NULL;
return buf;
}
or your own implementation using fgets and realloc:
char *getline(FILE * f)
{
size_t size = 0;
size_t len = 0;
size_t last = 0;
char *buf = NULL;
do {
size += BUFSIZ; /* BUFSIZ is defined as "the optimal read size for this platform" */
buf = realloc(buf, size); /* realloc(NULL,n) is the same as malloc(n) */
/* Actually do the read. Note that fgets puts a terminal '\0' on the
end of the string, so we make sure we overwrite this */
if (buf == NULL) return NULL;
fgets(buf + last, BUFSIZ, f);
len = strlen(buf);
last = len - 1;
} while (!feof(f) && buf[last] != '\n');
return buf;
}
Call it using
char *str = getline(stdin);
if (str == NULL) {
perror("getline");
exit(EXIT_FAILURE);
}
...
free(str);
More info
Firstly, gets() provides no way of preventing a buffer overrun. That makes it so dangerous it has been removed from the latest C standard. It should not be used. However, the usual usage is something like
char buffer[20];
gets(buffer); /* pray that user enters no more than 19 characters in a line */
Your usage is passing gets() a pointer to a pointer to a pointer to char. That is not what gets() expects, so your code would not even compile.
That element of prayer reflected in the comment is why gets() is so dangerous. If the user enters 20 (or more) characters, gets() will happily write data past the end of buffer. There is no way a programmer can prevent that in code (short of accessing hardware to electrocute the user who enters too much data, which is outside the realm of standard C).
To answer your question, however, the only ways involve allocating a buffer of some size, reading data in some controlled way until that size is reached, reallocating if needed to get a greater size, and continuing until a newline (or end-of-file, or some other error condition on input) is encountered.
malloc() may be used for the initial allocation. malloc() or realloc() may be used for the reallocation (if needed). Bear in mind that a buffer allocated this way must be released (using free()) when the data is no longer needed - otherwise the result is a memory leak.
use the getline() function, this will return the length of the line, and a pointer to the contents of the line in an allocated memory area. (be sure to pass the line pointer to free() when done with it )
"Reading an unknown length line from stdin in c with fgets"
Late response - A Windows approach:
The OP does not specify Linux or Windows, but the viable answers posted in response for this question all seem to have the getline() function in common, which is POSIX only. Functions such as getline() and popen() are very useful and powerful but sadly are not included in Windows environments.
Consequently, implementing such a task in a Windows environment requires a different approach. The link here describes a method that can read input from stdin and has been tested up to 1.8 gigabytes on the system it was developed on. (Also described in the link.)_ The simple code snippet below was tested using the following command line to read large quantities on stdin:
cd c:\dev && dir /s // approximately 1.8Mbyte buffer is returned on my system
Simple example:
#include "cmd_rsp.h"
int main(void)
{
char *buf = {0};
buf = calloc(100, 1);//initialize buffer to some small value
if(!buf)return 0;
cmd_rsp("dir /s", &buf, 100);//recursive directory search on Windows system
printf("%s", buf);
free(buf);
return 0;
}
cmd_rsp() is fully described in the links above, but it is essentially a Windows implementation that includes popen() and getline() like capabilities, packaged up into this very simple function.
if u want to input an unknown length of string or input try using following code.
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
int main()
{
char *m;
clrscr();
printf("please input a string\n");
scanf("%ms",&m);
if (m == NULL)
fprintf(stderr, "That string was too long!\n");
else
{
printf("this is the string %s\n",m);
/* ... any other use of m */
free(m);
}
getch();
return 0;
}
Note that %ms, %as are GNU extensions..

How to make backslash character not to escape

I don't know the title correctly addresses my problem or not. So, I will just go with it.
Here is the problem, I have to input a char array of a file path (in Windows) containing lots of backslashes in it, eg. "C:\myfile.txt" and return an unsigned char array of C-style file paths, eg. "C:\myfile.txt".
I tried to write a function.
unsigned char* parse_file_path(char *path);
{
unsigned char p[60];
int i,j;
int len = strlen(path);
for(i=0,j=0; i<len; i++, j++)
{
char ch = path[i];
if(ch==27)
{
p[j++]='\\';
p[j]='\\';
}
else
p[j] = path[i];
}
p[j]='\0';
return p;
}
The weird thing (for me) I am encountering is, here path contains only one backslash '\'. In order to get one backslash, I have to put '\' in path. This is not possible, cause path cannot contain '\'. When I call it like this parse_file_path("t\es\t \it), it returns
t←s it. But parse_file_path("t\\es\\t \\it") returns t\es\t \it.
How can I accomplish my task? Thanks in advance.
If I can just mention another problem with your code.
You are returning a local variable (your unsigned char p). This is undefined behavior. Consider declaring a char* p that you assign memory to dynamically using malloc and then returning p as you do. E.g. something like:
char* p = malloc(60);
A common practice is to use sizeof when allocating memory with malloc but here I've passed 60 directly as the C standard guarantees that a char will be 1 byte on all platforms.
But you have to free the memory assigned with malloc.
Or alternatively, you can change the function to take a buffer as an input argument that it then writes to. That way you can pass a normal array where you would call this function.
Regarding your slashes issue, here:
p[j++]='\\';
p[j]='\\';
Position j in p will be changed to \\, then j will be incremented and at the very next line you do the same for the succeeding char position. Are you sure you want the two assignments?
By the way if you are inputting the path from the command line, the escaping will be taken care of for you. E.g. consider the following code:
#include <stdio.h>
#include <string.h> /* for strlen */
#include <stdlib.h> /* for exit */
int main()
{
char path[60];
fgets(path, 60, stdin); /* get a maximum of 60 characters from the standard input and store them in path */
path[strlen(path) - 1] = '\0'; /* replace newline character with null terminator */
FILE* handle = fopen(path, "r");
if (!handle)
{
printf("There was a problem opening the file\n");
exit(1); /* file doesn't exist, let's quite with a status code of 1 */
}
printf("Should be good!\n");
/* work with the file */
fclose(handle);
return 0; /* all cool */
}
And then you run it and input something like:
C:\cygwin\home\myaccount\main.c
It should print 'Should be good!' (provided the file does exist, you can also test with 'C:\').
At least on Windows 7 with cygwin this is what I get. No need for any escapes as this is handled for you.

Enter custom file name to be read?

I want to allow users to type the name of any .txt file to be read/written.
This is my code :
printf("Enter .txt file name\n");
scanf("%s",&fname);
FILE *inputf;
inputf=fopen(&fname,"w");
Problem is this method does not work (having &fname) as a parameter.
I can imagine its because C needs "filename.txt" for it work ... even if I enter for example : "custom.txt", the program returns an error of "Storage block not big enough for this operation"
What is the correct method to accomplish this ?
Im using C and im pretty much using basic commands .. (not too advanced)
Thanks alot !!!
The scanf statement will try to store the filename entered as input into the memory, starting from the address passed as its 2nd argument. So you have to allocate/reserve some memory and pass its address to scanf.
As you have not mentioned the type of fname, let me list the possibilities and then answer you.
char fname;
The 2nd argument of scanf and the 1st argument of fopen, both need to be char *. So, passing address of fname or &fname is valid. But it has a problem.
When you declare 'char fname' you are reserving memory for only 1 char. When scanf tries to store the input filename, it will have to write more than 1 char. So eventually you end up overwriting some other memory.
char *fname;
In this case pass fname to both scanf and fopen, instead of '&fname'.
But you have to allocate some memory (e.g. using malloc), before using fname. Otherwise fname will contain some garbage address and scanf will try to overwrite some random memory.
So either declare fname as char fname[N] or char *fname = malloc(N+1); (where N is the maximum possible length of filename you would be entering).
And then, pass fname to both scanf and fopen as follows:
scanf("%s",fname);
inputf = fopen(fname,"w");
Defining fname as a char array, and assuming you expect the filename (without extension) as input (which means you need to append the extension to it):
char fname[128];
printf("Enter .txt file name\n");
scanf("%123s",fname);
strcat(fname,".txt");
FILE *inputf;
inputf=fopen(fname,"w");
Note that an input length check is added to avoid buffer overflow errors in scanf.
I think this can help
#include <stdio.h>
void read_name(char *);
int main(void)
{
char name[BUFSIZ];
char line[BUFSIZ];
FILE *f;
printf("Name ");
read_name(name);
if ( (f=fopen(name,"r"))==NULL)
return -1;
else
return 0;
fclose(f);
}
void read_name(char *s)
{
int i;
fgets(s,BUFSIZ,stdin);
for (i=0; s[i]!='\n'; i++);
s[i]='\0';
return;
}
Try inputf = fopen(fname,"w");.
Also if you want to just read a filename, you can just do sscanf(file,"%s",t) and it will store the filename into t !

Resources