I'm trying to read line from stdin and store it in a pointer array, when I print the array I get the last entered value, the other values are replaced by the last entered value. The if statement works the first time only, after that it comparison doesn't come true. How can I read a line into pointer array?
void read_line(int fd, char *s)
{
char line[100];
char *list[100];
int i = 0;
while ((read(1, line, 100)))
{
if (!strncmp(line, s, strlen(line) - 1))
break;
else
list[i++] = line;
}
i = 0;
while (list[i])
{
write(fd, list[i], strlen(list[i]));
i++;
}
}
I call the function
read_line(1, "exit");
//if I type more words before typing exit, then type exit program doesn't terminate
First you should ensure that read did not fail by checking its return value:
while ((read(1, line, 100)) > 0)
Then you must copy line content in list[i++] using strdup, otherwise all list items will end up with the last value pointed by line.
Finally, what do you expect to happen when read reaches the limit of your buffer? You might want to handle that as well.
Code could look like this:
#define SIZE 100
void read_line(int fd, char *s)
{
char line[SIZE];
char *list[SIZE] = { 0 };
int i = 0;
ssize_t retval;
while ((retval = read(1, line, SIZE)) > 0)
{
if (retval == SIZE)
// adapt to the desired behavior here... for now we'll abort
break;
else if (!strncmp(line, s, strlen(line) - 1))
break;
else
list[i++] = strdup(line);
memset(line, 0, SIZE);
}
i = 0;
while (list[i])
{
write(fd, list[i], strlen(list[i]));
i++;
}
}
Related
I got this piece of code:
void scanLinesforArray(FILE* file, char search[], int* lineNr){
char line[1024];
int line_count = 0;
while(fgets(line, sizeof(line),file) !=NULL){
++line_count;
printf("%d",line_count);
printf(line);
char *temp = malloc(strlen(line));
// strncpy(temp,line,sizeof(line));
// printf("%s\n",temp);
free(temp);
continue;
}
}
This will print all lines of the file, but as soon as I uncomment the strncpy(), the program just stops without error.
Same happens as soon as I use strstr() to compare the line to my search variable.
I tried the continue statement and other redundant things, but nothing helps.
Many problems:
Do not print a general string as a format
Code risks undefined behavior should the string contain a %.
// printf(line); // BAD
printf("%s", line);
// or
fputs(line, stdout);
Bad size
strncpy(temp,line,sizeof(line)); is like strncpy(temp,line, 1024);, yet temp points to less than 1024 allocated bytes. Code attempts to write outside allocated memory. Undefined behavior (UB).
Rarely should code use strncpy().
Bad specifier
%s expects a match string. temp does not point to a string as it lacks a null character. Instead allocated for the '\0'.
// printf("%s\n", temp);`.
char *temp = malloc(strlen(line) + 1); // + 1
strcpy(temp,line);
printf("<%s>", temp);
free(temp);
No compare
"Can't compare Lines of a file in C" is curious as there is no compare code.
Recall fgets() typically retains a '\n' in line[].
Perhaps
long scanLinesforArray(FILE* file, const char search[]){
char line[1024*4]; // Suggest wider buffer - should be at least as wide as the search string.
long line_count = 0; // Suggest wider type
while(fgets(line, sizeof line, file)) {
line_count++;
line[strcspn(line, "\n")] = 0; // Lop off potential \n
if (strcmp(line, search) == 0) {
return line_count;
}
}
return 0; // No match
}
Advanced: Sample better performance code.
long scanLinesforArray(FILE *file, const char search[]) {
size_t len = strlen(search);
size_t sz = len + 1;
if (sz < BUFSIZ) sz = BUFSIZ;
if (sz > INT_MAX) {
return -2; // Too big for fgets()
}
char *line = malloc(sz);
if (line == NULL) {
return -1;
}
long line_count = 0;
while (fgets(line, (int) sz, file)) {
line_count++;
if (memcmp(line, search, len) == 0) {
if (line[len] == '\n' || line[len] == 0) {
free(line);
return line_count;
}
}
}
free(line);
return 0; // No match
}
So I am trying to take the users input using read(). The while loop runs until ctrl+d is entered in linux terminal and the input is stopped. I wrote some code and it works, but the problem I am running into is that I don't know how to take exactly whats in the input and how to use it.
In the code below, right after if(buff[offset] == '\n') (runs the given input, and after its finished clears the buffer and moves on to the next input) I don't know how to go into the 3 possible cases of input and also I don't know how to get the number from the second case:
"e" - exit the program
"p N" - do something, N is a number which I also need to get into a variable (I think that buff[offset-1] should get the number but I am not quite sure.)
everything else - print a message
Code:
int fd = 0; // set read() to read from STDIN_FILENO, because it's number is 0
const size_t read_size = 1; // set chunk size
size_t size = read_size;
size_t offset = 0;
size_t res = 0;
char *buff = malloc(size+1);
*buff = '\0';
while((res = read(fd, buff + offset, read_size)) > 0) // read from stdin and save to buff
{
if(buff[offset] == '\n')
{ // THIS PART
buff[offset] = '\0';
if(buff == "e")
{
// exit the program
return 0;
}
else if(buff == "p")
{
// do sth
}
else
{
// print a message
}
// reset the buffer (free its memory and allocate new memory for the next input)
offset = 0;
size = read_size;
free(buff);
buff = NULL;
buff = malloc(size + 1);
*buff = '\0';
}
else
{
offset += res;
buff[offset] = '\0';
if (offset + read_size > size)
{
size *= 2;
buff = realloc(buff, size+1);
}
}
}
If this isn't possible with read() I can try with something else like fgets() perhaps?
if(buff == "p")
This line checks if the string buff is stored at the same address as the string "p". However, you don't want to check if the strings are stored at the same address, but if they have the same content.
... how would strcmp() work ...
Using strcmp() you can check if two strings are identical:
if(!strcmp(buff, "e"))
"p N" ... since the number N could be any number?
Simply check if the first character of buff is 'p':
if(buff[0] == 'p')
Note that single quotes (') are used instead of double quotes (") for character constants (in contrast to string constants).
As I described in my comment above, here is a full answer:
int main(void)
{
char line[100], cmd;
int num, cnt;
while(fgets(line,sizeof(line),stdin)) != EOF) {
cnt = sscanf(line," %c %d",&cmd,&num);
if (cnt == 0) {
// empty line, no command
} else if (cmd == 'e') {
// exit the program
break;
} else if (cmd == 'p' && cnt == 2) {
// no something with num
} else {
// print a message
}
}
return 0;
}
I have this C function which basically takes the input from the user and saves it in a static char[]. Then it checks whether it is empty. If it is empty then it prints an error, else appends a file extension to it. Here's the piece of code I'm concerned about:
const char* text_entry(){
char c;
while((c=getchar())!='\n' && c!=EOF);
static char input[20];
fgets(input, sizeof(input),stdin);
input[strcspn(input,"\n")]=0;
if(strlen(input)>0){
strncat(input, ".txt",4);
return input;
}
printf("Error! no text entered!");
return NULL;
}
My concern is that I want to remove the IF statement from it, making it more DRY. How can I do that?
The "DRY" is in the usage of strcspn(), strlen() and strncat(). You can avoid them by
size_t l = strcspn(input, "\n");
bool ok = false;
if (l == 0) {
fprintf(stderr, "error: too short\n");
} else if (l > sizeof input - sizeof(".txt")) {
fprintf(stderr, "error: too long\n");
} else {
strcpy(input + l, ".txt");
ok = true;
}
return ok ? input : NULL;
This is a minimal loop version. It doescheck* if the result fits into the input buffer.
Note:I'm not saying this is the best way todo it. But it avoids multiple scans over the input. (the gain in performance is mostly futile...)
#include <stdio.h>
#include <string.h>
char* text_entry(){
int ch;
size_t pos;
static char input[20];
//this is dubious: it consumes characters upto and including the first '\n'
while((ch=getchar())!='\n' && ch!=EOF) {;}
for (pos=0; pos < sizeof input; pos++) {
ch=getchar();
if (ch =='\n' || ch==EOF) break;
input[pos] = ch;
}
// noinput
if(!pos) return NULL;
// input too large: cannot append suffix
if(pos + sizeof ".txt" > sizeof input) return NULL;
memcpy(input+pos, ".txt", sizeof ".txt" );
// fprintf(stderr, "[%zu]\n",strlen(input));
return input;
}
int main(void)
{
char *result;
while(1) {
result = text_entry();
if (!result) continue;
printf("%s\n", result);
}
return 0;
}
I did not shorten your code, but I think you might find this solution more resilient, using the libreadline -lreadline
Before your returned a buffer which was declared on the stack, which means that it is unallocated on return, but yet you return its address. This is a bug.
I fixed it and gave you the opportunity to take a buffer and buffer size in parameters, as well as the extension, so the caller manages the memory for you as well as his maximum string length needs.
The loop now handles maximum input size well. It might not be shorter, but it should prove resilient.
/*
** Args:
** input_buffer: a buffer that will be whiped then filled with the user input
** buffer_size: the total size of the input buffer (maximum result length)
** extension: a dotted file type extension
**
** Returns: 0 on error or the input_buffer
*/
const char* text_entry(char *buffer, size_t buffer_size, char *extension) {
int maxsize = buffer_size - strlen(extension) - 1;
char *user_input;
int user_input_len;
do {
user_input = readline("Enter a file name: ");
user_input_len = strlen(user_input);
if (user_input_len > maxsize) {
printf("The name is too long! %d characters maximum. Try again.\n", maxsize);
free(user_input);
}
} while (!user_input_len || user_input_len > maxsize);
strcpy(buffer, user_input);
free(user_input);
strcat(buffer, extension);
printf("%s\n", buffer);
return buffer;
}
I'm implementing getchar(), I have two issues with the BUFF_SIZE, when I use 1024 my getchar() reads all the characters and return one character discards the rest. This doesn't work inside a loop.
#ifndef BUFF_SIZE
#define BUFF_SIZE 1024
#endif
int my_getchar(void)
{
static char buff[BUFF_SIZE];
static char *chr;
int ret;
if ((ret = read(STDIN_FILENO, buff, BUFF_SIZE)) > 0)
{
chr = buff;
return (*chr);
}
return (EOF);
}
int get_line(char **line)
{
char *text = malloc(sizeof(char) * 4092);
int position = 0;
int c;
if (!text)
return (-1);
while (1)
{
c = my_getchar();
if (c == EOF || c == '\n')
{
text[position] = '\0';
*line = text;
return (1);
}
else
text[position] = c;
position++;
}
return (0);
}
When I set BUFF_SIZE to 1, it works fine inside a loop, and doesn't work well outside a loop. How can I solve this?
int main()
{
//comment out to test.
//char *sp;
//sp = (char *)malloc(sizeof(char) * 4092);
//get_line(&sp); // this function calls my_getchar inside a while loop.
printf("%c\n", my_getchar()); //calling my_getchar() outside a loop
// printf("%s\n", sp);
return 0;
}
Your my_getchar() skips BUF_SIZE - 1 characters on each invokation. Every time you call it - it reads BUF_SIZE characters from stdin (or less if we are at the end of the file) and returns the first character. All other are dropped, because when you call it next time it reads BUF_SIZE chars again instead of returning of the next char from previously read buffer.
When you set BUFF_SIZE to 1, you are calling read like this:
read(STDIN, buff, 1)
But, since STDIN is connected to a terminal, read() is allowed to wait for a line to be typed in before returning the first character. So, the function is behaving correctly (as written).
The good news is that if you use printf("%c\n", getchar());, you will see that getchar() behaves in the same way.
The main problem with your version is that it is built on top of read() -- the real getchar() is in <stdio.h> and meant to be mixable with the calls in there that use FILE* and its buffering. So, unless you are reimplementing all of stdio, it probably makes sense to build it on its primitives (e.g. fread).
If you are reimplementing all of <stdio>, then you should be implementing some equivalent to FILE, which is where the buffers would live (not in static variables inside of functions).
OP's code reads BUFF_SIZE chars, and returns only 1.
Need to selectively read. Only read when previous characters are comsumed.
Note: Better to pass in a structure pointer to the state (buff, index,count) than to use static variables, but staying with OP's approach:
int my_getchar(void) {
static unsigned char buff[BUFF_SIZE];
static int index = 0;
static int count = 0;
if (index >= count) {
index = 0;
count = read(STDIN_FILENO, buff, BUFF_SIZE);
if (count == 0) return EOF; // end-of-file
if (count < 0) return EOF; // I/O error
}
return buff[index++];
}
Note: important to read an unsigned char buffer to distinguish EOF from input charterers.
Depending on STDIN_FILENO attributes, a return value of 0 from read() may simple imply input not presently available. Then use
// count = read(STDIN_FILENO, buff, BUFF_SIZE);
// if (count == 0) return EOF; // end-of-file
do {
count = read(STDIN_FILENO, buff, BUFF_SIZE);
} while (count == 0);
There is issue in your my_getchar function. Issue is here:
/* When you do return *char only first char is returned */
if ((ret = read(STDIN_FILENO, buff, BUFF_SIZE)) > 0)
{
chr = buff;
return (*chr);
}
There you are doing return (*chr); you return only the first char in buff, so that way a \n is not returned and so your get_line is affected as it depends on a \n returned. This can be fixed this by adding an additional static variable to track the position of latest char returned, as below.
int my_getchar(void)
{
static char buff[BUFF_SIZE];
static char *chr;
static int pos = 0; /* New static variable to track position */
static int ret = 0; /* Changed this to static */
if (pos >= ret) { /* if all data in buffer has been returned */
if ((ret = read(STDIN_FILENO, buff, BUFF_SIZE)) > 0)
{
chr = buff;
pos = 0;
return *(chr + pos++); /* return one char and update pos */
} else { /* if no more to read from stdin */
return EOF;
}
} else { /* if data still in buffer */
return *(chr + pos++); /* return one char and update pos */
}
}
Now, my_getchar will return a \n when pos reaches the postition of \n in the buffer, so get_line will get all characters before the new line character.
Your code reads as many characters as possible:
read(STDIN_FILENO, buff, BUFF_SIZE)
up to BUFF_SIZE and then only returns the first character.
For that to work as intended, your buffer size needs to be 1.
I want to make a function that reads a line of your choice, from a given text file. Moving on to the function as parameters (int fd of the open, and int line_number)
It must do so using the language C and Unix system calls (read and / or open).
It should also read any spaces, and it must not have real limits (ie the line must be able to have a length of your choice).
The function I did is this:
char* read_line(int file, int numero_riga){
char myb[1];
if (numero_riga < 1) {
return NULL;
}
char* myb2 = malloc(sizeof(char)*100);
memset(myb2, 0, sizeof(char));
ssize_t n;
int i = 1;
while (i < numero_riga) {
if((n = read(file, myb, 1)) == -1){
perror("read fail");
exit(EXIT_FAILURE);
}
if (strncmp(myb, "\n", 1) == 0) {
i++;
}else if (n == 0){
return NULL;
}
}
numero_riga++;
int j = 0;
while (i < numero_riga) {
ssize_t n = read(file, myb, 1);
if (strncmp(myb, "\n", 1) == 0) {
i++;
}else if (n == 0){
return myb2;
}else{
myb2[j] = myb[0];
j++;
}
}
return myb2;
}
Until recently, I thought that this would work but it really has some problems.
Using message queues, the string read by the read_line is received as a void string ( "\0" ). I know the message queues are not the problem because trying to pass a normal string did not create the problem.
If possible I would like a fix with explanation of why I should correct it in a certain way. This is because if I do not understand my mistakes I risk repeating them in the future.
EDIT 1. Based upon the answers I decided to add some questions.
How do I end myb2? Can someone give me an example based on my code?
How do I know in advance the amount of characters that make up a line of txt to read?
EDIT 2. I don't know the number of char the line have so I don't know how many char to allocate; that's why I use *100.
Partial Analysis
You've got a memory leak at:
char* myb2 = (char*) malloc((sizeof(char*))*100);
memset(myb2, 0, sizeof(char));
if (numero_riga < 1) {
return NULL;
}
Check numero_riga before you allocate the memory.
The following loop is also dubious at best:
int i = 1;
while (i < numero_riga) {
ssize_t n = read(file, myb, 1);
if (strncmp(myb, "\n", 1) == 0) {
i++;
}else if (n == 0){
return NULL;
}
}
You don't check whether read() actually returned anything quick enough, and when you do check, you leak memory (again) and ignore anything that was read beforehand, and you don't detect errors (n < 0). When you do detect a newline, you simply add 1 to i. At no point do you save the character read in a buffer (such as myb2). All in all, that seem's pretty thoroughly broken…unless…unless you're trying to read the Nth line in the file from scratch, rather than the next line in the file, which is more usual.
What you need to be doing is:
scan N-1 lines, paying attention to EOF
while another byte is available
if it is newline, terminate the string and return it
otherwise, add it to the buffer, allocating space if there isn't room.
Implementation
I think I'd probably use a function get_ch() like this:
static inline int get_ch(int fd)
{
char c;
if (read(fd, &c, 1) == 1)
return (unsigned char)c;
return EOF;
}
Then in the main char *read_nth_line(int fd, int line_no) function you can do:
char *read_nth_line(int fd, int line_no)
{
if (line_no <= 0)
return NULL;
/* Skip preceding lines */
for (int i = 1; i < line_no; i++)
{
int c;
while ((c = get_ch(fd)) != '\n')
{
if (c == EOF)
return NULL;
}
}
/* Capture next line */
size_t max_len = 8;
size_t act_len = 0;
char *buffer = malloc(8);
int c;
while ((c = get_ch(fd)) != EOF && c != '\n')
{
if (act_len + 2 >= max_len)
{
size_t new_len = max_len * 2;
char *new_buf = realloc(buffer, new_len);
if (new_buf == 0)
{
free(buffer);
return NULL;
}
buffer = new_buf;
max_len = new_len;
}
buffer[act_len++] = c;
}
if (c == '\n')
buffer[act_len++] = c;
buffer[act_len] = '\0';
return buffer;
}
Test code added:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
extern char *read_nth_line(int fd, int line_no);
…code from main answer…
int main(void)
{
char *line;
while ((line = read_nth_line(0, 3)) != NULL)
{
printf("[[%s]]\n", line);
free(line);
}
return 0;
}
This reads every third line from standard input. It seems to work correctly. It would be a good idea to do more exhaustive checking of boundary conditions (short lines, etc) to make sure it doesn't abuse memory. (Testing lines of lengths 1 — newline only — up to 18 characters with valgrind shows it is OK. Random longer tests also seemed to be correct.)