I try to use read() to get some characters from file just for learning this API. I have create a file called "file" in the same directory and it is content:
1:2:ab:cd:ef
Here is the code:
#include <stdio.h>
#include <error.h>
int read_indent(int sockfd){
int sport, cport;
char user[3], rtype[3], addinfo[3];
char buffer[4+4+3+3+3+1];
if(read(sockfd, buffer, sizeof(buffer)) <= 0) {
perror("read: %m");
return -1;
}
buffer[sizeof(buffer)-1] = '\0';
sscanf(buffer, "%d:%d:%s:%s:%s", &sport, &cport, rtype, user, addinfo);
printf("%d:%d:%s:%s:%s", sport, cport, rtype, user, addinfo);
return 0;
}
int main(){
FILE *file_pt = fopen("file", "r");
if(file_pt == NULL) { printf("fopen error\n"); return -1;}
char buf[128];
int a = read_indent(fileno(file_pt));
fclose(file_pt);
return 0;
}
My printf returns me
1:2:ab:cd:ef::xPvx
where x is some garbage character I cannot recognize. What is the reason for this? int is 4 bytes in my system.
One issue is that you didn't specify a width for the %s parameters. This means that it matches up until the first whitespace character. There are no whitespace characters in your string, so the first %s matches until the end, leaving only garbage data after your string to fill the other variables.
Try this:
sscanf(buffer, "%d:%d:%2s:%2s:%2s", &sport, &cport, rtype, user, addinfo);
The other issue is that you don't null-terminate your buffer properly, read returns the number of characters read - add a null after that.
char buffer[4+4+3+3+3+1];
The buffer is bigger than what you plan to read and that's ok, but:
buffer[sizeof(buffer)-1] = '\0';
This is wrong, add the \0 at size+1 , where size is what you get back with read(), the actual number of bytes read.
See here:
The value returned may be less than nbyte if the number of bytes left in the file is less than nbyte, if the read() request was interrupted by a signal, or if the file is a pipe or FIFO or special file and has fewer than nbyte bytes immediately available for reading.
char buffer[4+4+3+3+3+1];
if(read(sockfd, buffer, sizeof(buffer)) <= 0) {
//....
}
buffer[sizeof(buffer)-1] = '\0';
The read function does not add \0 to the buffer after reading. But you read just 12 bytes, and your buffer size is 18. So you still have 5 bytes of garbage in your buffer. This gets added to the last string you read.
Related
Here's my task, below is most of the code done and finally my specific question
Write a program that reads strings and writes them to a file. The string must be dynamically allocated and the string can be of arbitrary length. When the string has been read it is written to the file. The length of the string must be written first then a colon (‘:’) and then the string. The program stops when user enters a single dot (‘.’) on the line.
For example:
User enters: This is a test
Program writes to file: 14:This is a test
Hint: fgets() writes a line feed at the end of the string if it fits in the string. Start with a small length, for example 16 characters, if you don’t see a line feed at the end then realloc the string to add more space and keep on adding new data to the string until you see a line feed at the end. Then you know that you have read the whole line. Then remove any ‘\r’ or ‘\n’ from the string and write the string length and the string to the file. Free the string before asking for a new string.
MY CODE:
#pragma warning(disable: 4996)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NAME_SZ 256
int main()
{
char key[] = ".\n";
char* text;
text = (char*)malloc(MAX_NAME_SZ);
if (text == NULL)
{
perror("problem with allocating memory with malloc for *text");
return 1;
}
FILE* fp;
fp = fopen("EX13.txt", "w");
if (fp == NULL)
{
perror("EX13.txt not opened.\n");
return 1;
}
printf("Enter text or '.' to exit: ");
while (fgets(text, MAX_NAME_SZ, stdin) && strcmp(key, text))
{
fprintf(fp, "%ld: %s", strlen(text) - 1, text);
printf("Enter text or '.' to exit: ");
}
free((void*)text);
fclose(fp);
puts("Exit program");
return 0;
}
SPECIFIC QUESTION:
How can I make the program to allow arbitrarily long lines so there shouldn't be no limit at all for line length? Thanks
You could declare a pointer to char, read char by char and keep using reallocating the pointer until you get to the '\n':
int main()
{
char key[] = "."; //Excluded the \n since I'm not using fget
char* text;
FILE* fp;
fp = fopen("EX13.txt", "w");
if (fp == NULL)
{
perror("EX13.txt not opened.\n");
return 1;
}
printf("Enter text or '.' to exit: ");
int cont = 0;
while (1) //read all chars
{
if(!cont) //if it is the first, allocate space for 1
text = (char*) malloc(sizeof (char));
else //otherwise increase the space allocated by 1
text = (char*) realloc(text, (cont + 1) * sizeof(char));
scanf("%c", &text[cont]); //read a single char
if(text[cont] == '\n') //see if it is the end of line
{
text[cont] = 0; //if it is the end of line, then it is the end of the string
if(!strcmp(key, text)) //if the string is just a dot, end the loop
break;
fprintf(fp, "%ld: %s\n", cont, text);
printf("Enter text or '.' to exit: ");
cont = 0; //restarting the counter for the next input
free(text); // freeing after each iteration. you can optimize to maintain the space and only increase after getting to a bigger string than the previous you had so far
}
else //if it is not the end of the string, increase its size by 1
cont++;
}
free((void*)text);
fclose(fp);
puts("Exit program");
return 0;
}
Suggest using getline()
This seems to be a class room assignment, so I will not be writing the code for you.
Note: for the getline() function to be visible in linux, at the beginning of your code, you will need a statement similar to:
#define _GNU_SOURCE
or
#define _POSIX_C_SOURCE 200809L
getline(3)
NAME
getdelim, getline -- get a line from a stream
LIBRARY
Standard C Library (libc, -lc)
SYNOPSIS
#include <stdio.h>
ssize_t
getdelim(char ** restrict linep, size_t * restrict linecapp,
int delimiter, FILE * restrict stream);
ssize_t
getline(char ** restrict linep, size_t * restrict linecapp,
FILE * restrict stream);
DESCRIPTION
The getdelim() function reads a line from stream, delimited by the char-
acter delimiter. The getline() function is equivalent to getdelim() with
the newline character as the delimiter. The delimiter character is
included as part of the line, unless the end of the file is reached.
The caller may provide a pointer to a malloced buffer for the line in
*linep, and the capacity of that buffer in *linecapp. These functions
expand the buffer as needed, as if via realloc(). If linep points to a
NULL pointer, a new buffer will be allocated. In either case, *linep and
*linecapp will be updated accordingly.
RETURN VALUES
The getdelim() and getline() functions return the number of characters
written, excluding the terminating NUL character. The value -1 is
returned if an error occurs, or if end-of-file is reached.
EXAMPLES
The following code fragment reads lines from a file and writes them to
standard output. The fwrite() function is used in case the line contains
embedded NUL characters.
char *line = NULL;
size_t linecap = 0;
ssize_t linelen;
while ((linelen = getline(&line, &linecap, fp)) > 0)
fwrite(line, linelen, 1, stdout);
ERRORS
These functions may fail if:
[EINVAL] Either linep or linecapp is NULL.
[EOVERFLOW] No delimiter was found in the first SSIZE_MAX characters.
These functions may also fail due to any of the errors specified for
fgets() and malloc().
Note: you will need to pass to free() the line, when the code is through with it, to avoid a memory leak.
Note: to remove any trailing '\n' you can use:
line[ strcspn( line, "\n" ) ] = '\0';
Note: after removing any trailing '\n' you can use:
size_t length = strlen( line );
To get the length of the line in bytes.
Then print that length and the line using:
printf( "%zu:%s", length, line );
The fgets() function has two problems. The first is that, if the size of the line is longer than that of the passed buffer, the line is truncated. The second is that, if the line read from the file has embedded '\0' characters, then there is no way to know the actual length of the line. I would like to get a replacement for fgets() that dynamically allocates the space for the line read and also provides the size of the line read. I have written the code for dynamically allocating the space. I am unable to figure out how to get the size of the line read. I am a beginner. Thank you so much.
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <errno.h>
char *myfgets(FILE *fptr, int *size);
char *myfgets(FILE *fptr, int *size) {
char *buffer;
char *ret;
buffer = (char *)malloc((*size) * sizeof(char));
if (buffer == NULL)
error(1, 0, "No memory available\n");
ret = fgets(buffer, *size, fptr);
if (ret == NULL)
error(1, 0, "Error in reading the file\n");
return ret;
}
int main(int argc, char *argv[]) {
char *file;
FILE *fptr;
int size;
char *result;
if (argc != 3)
error(1, 0, "Too many or few arguments <File_name>, <Number of bytes to read>\n");
file = argv[1];
size = atoi(argv[2]);
fptr = fopen(file, "r");
if (fptr == NULL)
error(1, 0, "Error in opening the file\n");
result = myfgets(fptr, &size);
printf("The line read is :%s", result);
free(result);
return 0;
}
Use getline(3) to read a complete line of unknown length. It allocates memory as needed to hold it all.
The function can deal with 0 bytes in the line being read too. From the linked man page (emphasis added):
On success, getline() and getdelim() return the number of characters read, including the delimiter character, but not including the terminating null byte ('\0'). This value can be used to handle embedded null bytes in the line read.
So you just have to save its return value instead of using strlen().
You have correctly identified 2 issues in fgets(), but your proposed alternative does not address either of them as you still call fgets().
You should write a loop, calling getc() repeatedly until you get EOF or '\n' and you would store the bytes read into an allocated array, reallocating as needed.
Here is a simplistic version:
// Read a full line from `fptr`
// - return `NULL` at end of file or upon read error like `fgets()`.
// - otherwise return a pointer to an allocated array containing the
// characters read, up to and including the newline and a null terminator.
// - store the number of bytes read into *plength.
// - the buffer is null terminated, and it may contain embedded null bytes
// if such bytes were read from the file
char *myfgets(FILE *fptr, size_t *plength) {
size_t length = 0;
char *buffer = NULL, *newp;
int c;
for (;;) {
if (c = getc()) == EOF) {
if (!feof(fptr)) {
/* read error: discard data read so far and return NULL */
free(buffer);
buffer = NULL;
length = 0;
}
break;
}
if ((newp = realloc(buffer, length + 2)) == NULL) {
free(buffer);
error(1, 0, "Out of memory for realloc\n");
return NULL;
}
buffer = newp;
buffer[length] = c;
length++;
if (c == '\n')
break;
}
if (length != 0) {
buffer[length] = '\0';
}
*plength = length;
return buffer;
}
Various approaches for a "fixed" fgets():
1) Use the non-C library standard getline() as suggested by #Shawn. Commonly available in *nix and source code easy enough to find. It unfortunately obliges a new type: ssize_t.
2) Roll your own getc() code #chqrlie. Corner cases can be tricky.
3) Repeatedly call fgets() as needed. Pre-fill the buffer with '\n' and look for the first occurrence of '\n', its position, next character to help determine length. (There are only a few cases to consider)
4) Repeatedly call scanf("%99[^\n]%n", buf100, &n) and getc() for the '\n' as needed. Look at the return value and n to determine length.
5) Likely others
A good functional test of the design is how well did it report the cases:
Happy path: a line was read, memory allocated, no problems.
End-of-file: Nothing read due to end of file.
Out-of-memory.
Input error occurred.
Other considerations:
Do you really want to save a '\n'?
Performance.
As for me with "dynamically allocates the space" with no limit, code introduces the ability for a nefarious user to overwhelm memory resources by entering a pathologically long line. Rather than give such ability to a user, I recommend to limit input to a sane bound. Excessively long input is an attack that should be detected, not enabled.
So I would start with
char *myfgets(FILE *fptr, size_t limit, size_t *size) {
Until now, whenever I wanted to get user input from stdin I used scanf() but this time I can't and have to use read().
Usually, to get input from stdin using read I use:
char buf[128];
read(0, buf, sizeof(buf));
But this time I don't have any length limit to the input and I want to allow input with arbitrary size. In the past I used scanf for this, like so:
char *user_input;
scanf("%ms", &user_input);
How can I do this with read()?
Note: safety isn't important here
The function read returns the number of read bytes. You can take advantage of this information and loop until you read 0 bytes, that is, read returns 0.
char buf[BUF_SIZE]; // Set BUF_SIZE to the maximum number of character you expect to read (e.g. 1000 or 10000 or more).
int bytes_to_read, total_read_bytes, read_bytes;
// Number of bytes to read at each iteration of the loop.
bytes_to_read = 128;
// The following variable counts the number of total read bytes.
total_read_bytes = 0;
while ((read_bytes = read(0, buf + total_read_bytes, bytes_to_read) != 0) {
if (read_bytes < 0) {
// read() may return -1. You can look at the variable errno to
// have more details about the cause of the error.
return -1;
}
total_read_bytes += read_bytes;
}
Notice that read does not automatically append the null terminator \0 to buf, that is, buf is not a string until you explicitly add \0 at the end of it.
...
// Making buf a string.
buf[total_read_bytes] = '\0';
...
one way is run a loop with getchar() function and keep rading the characters into an array. check the array size with each iteration ,Once the array is full, reallocate it to a larger size. OR use the The getline() function.
link for getline()
Check the below programme .
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char *lines = NULL;
size_t n = 0;
ssize_t res = getline(&line, &n, stdin);
free(line);
}
So I am opening a file that contains cards data for a card game I am designing for my assignment, basically each line contains 104 characters and each line is equal to a deck of card.
I'm using a char **** because of
number of decks
num of players (4)
num of cards (13)
card is represented like 9H, 3D means nine of hearts and three of diamonds, so it uses 2 characters.
I want to use fgets() to read multiple lines but I'm not sure if this works...
for loop is just the way how the deckfile is set, I just want to know if the fgets will go to the next line when it hits \n...
di->cards = (char ****)malloc(sizeof(char***) * di->numDecks);
for (i = 0; i < di->numDecks; i++) {
di->cards[i] = (char ***)malloc(sizeof(char**) * 4);
for (j = 0; j < 4, j++) {
di->cards[i][j] = (char **)malloc(sizeof(char*) * 13);
for (k = 0, k < 13, k++) {
di->cards[i][j][k] = (char *)malloc(sizeof(char) * 3);
}
}
}
for (i = 0; i < di->numDecks, i++) {
for (j = 0; j < 13, j++) {
for (k = 0; k < 4; k++) {
while ((fgets(cards[i][k][j], 3, di->deckFile)) != NULL);
}
}
}
fgets() is often called in a loop, such as this:
FILE *fp;
char buf[260];// or char *buf, then use malloc - make index size appropriate length for anticipated line len.
fp = fopen("C:\\somedir\\card.txt", "r");
while(fgets(buf, sizeof(buf), fp)) //where sizeof(buf) is the length of
//line you anticipate reading in.
{
//do something with buf here;
//The input of fgets will be NULL as soon
//as its input fp has been fully read, then exit the loop
}
fclose(fp);
Your statement while((fgets(cards[i][k][j], 3, di->deckFile)) != NULL);
has a couple of issues, one is the ; at the end. It will just loop on this one line, and not give you a chance to do anything with the line that is read before it reads the next one. Also, 3 is probably not the length of line you want to read, is it? 3 is the buffer size that will hold your card data, but the line you read from the file will be longer.
So, in addition to these points, consider the other ideas in the comments, and make changes as indicated.
[EDIT] modified to read a file with "AS3D4C...(52 cards)" 4 lines
It will fill in enough spaces for 4 decks of cards. You can use this to
see how to read in the data. strtok (used before) works only when there
are delimiters, which if you can, I would recommend using instead of
long strings. Hope this helps.
(Note, I used no [mc]alloc()'s in this example.
#include <ansi_c.h>
#define FILENAME "C:\\dev\\play\\cards.txt"
int main()
{
int i, j;
FILE *fp;
char buf[260];// or char *buf, then use malloc - make index size appropriate length for anticipated line len.
char *cardTok;
char cards[208][3]; //4 players, 4 decks, each card is 3 bytes (eg. [A|S|\0], they all need a null termination)
memset(cards, 0, 208*3);
fp = fopen(FILENAME, "r");
j = 0;
while(fgets(buf, sizeof(buf), fp)) //where buf len is initialized at 260
//and well over the anticipated 104/line, including \n etc.
{ //note, fgets() will read up to n-1 characters and place a \0 at the end
//but will stop reading sooner if it sees end of line.
for(i=0;i<52;i++) //i is card number
{
cards[i+j][0] = buf[2*i+0];
cards[i+j][1] = buf[2*i+1];
cards[i+j][2] = 0;
}
j+=52;
}
fclose(fp);
}
My text file looked like this:
9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD3D4SQhKD1H9H3D4SQhKDKD1H9H3D4SQhKD
6C9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD3D4SQhKD1H9H3D4SQhKDKD1H9H3D4SQh
2D9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD3D4SQhKD1H9H3D4SQhKDKD1H9H3D4SQh
3S9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H1H9H3D4SQhKD1H9H3D4SQhKD3D4SQhKD1H9H3D4SQhKDKD1H9H3D4S
#include <stdio.h>
char *fgets(char *s, int size, FILE *stream);
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline.
be careful with this : If a newline is read, it is stored into the buffer. A terminating null byte ('\0') is stored after the last character in the buffer.
When you want to compare line , before you need to remove \n before null byte.
If you want to read single line.
char line[100]; // here you can use char *line=malloc(100);
fgets(line,sizeof line,file_stream);
printf("%s\n",line);
if you want to read multiple lines
char lines[20][100]; // here you can use char **lines=malloc(100);
i=0;
//if you use **lines allocate size for all lines with the loop or else you can allocate size inside loop and then read.
while((fgets(lines[i],SIZE_ALLOCATED_FOR_LINE,file_stream)!=NULL) && (i<20))
{
printf("%s\n",line[i++]);
}
The documentation says,
char *fgets( char *str, int count, FILE *stream );
char *fgets( char *restrict str, int count, FILE *restrict stream );
Reads at most count - 1 characters from the given file stream and
stores them in str. The produced character string is always
NULL-terminated. Parsing stops if end-of-file occurs or a newline
character is found, in which case str will contain that newline
character.
Also,
The return value is NULL on failure.
If the failure has been caused by EOF condition, additionally sets the eof indicator (see feof()) on stdin. If the failure has been caused by some other error, sets the error indicator (see ferror()) on stdin.
Also check for feof to ensure NULL was obtained due to EOF
If you want to take the fgets input and input all of it into an array of arrays or string array how could you do that. I have tried different things but get seg faults
This is the code given by http://www.gravitech.us/7segmentshield.html.
void SerialMonitorPrint (byte Temperature_H, int Decimal, bool IsPositive)
{
Serial.print("The temperature is ");
if (!IsPositive)
{
Serial.print("-");
}
Serial.print(Temperature_H, DEC);
Serial.print(".");
Serial.print(Decimal, DEC);
Serial.print(" degree C");
Serial.print("\n\n");
}
But when I try to read data from serial port, I found that I read data character by character.
UPDATE
while(1)
{
char buffer[100];
int chars_read = read(fd, &buffer, sizeof(buffer));
buffer[chars_read] = '\0';
printf("%s", buffer);
}
So how can I read line by line?
The while loop does not necessarily read character by character, but it might return you one character at a time for each read based on the serial port device and the rate of transmission.
I've made some changes to fix few bugs in your while loop:
while(1)
{
char buffer[100];
ssize_t length = read(fd, &buffer, sizeof(buffer));
if (length == -1)
{
printf("Error reading from serial port\n");
break;
}
else if (length == 0)
{
printf("No more data\n");
break;
}
else
{
buffer[length] = '\0'
printf("%s", buffer);
}
}
List of changes:
Check the return value from read
I'm assuming when read fails or returns 0, it means no more data to read and breaks the while loop execution. Modify this behavior as per your needs.
Append a '\0' character before printing, otherwise printf would be printing garbage values in the buffer.
Comments:
Do not worry about lines, the read should return a \n character in the buffer which printf would interpret as a newline when you print it out.
If you're actually only interested in grabbing a line and storing it somewhere, you need to read and append to another buffer until you get a \n in the buffer, and also need to handle multiple \n within the same buffer implying multiple lines.
You can't guarantee that a single call to read will give you exactly one line of text. You need to buffer the data. The easiest way to do this is to read exactly one character at a time, and stop when you reach a newline character. If you want to read as many characters as possible each time, the buffering code becomes more complicated.
Try this to start:
char buffer[100] = {0};
int pos = 0;
while( pos < 99 ) {
read(fd, buffer+pos, 1); // Note you should be checking the result
if( buffer[pos] == '\n' ) break;
pos++;
}
// Normally you would null-terminate string, but noticed I initialised the
// buffer to all zeroes at the beginning. So this is not strictly necessary.
// However, in this case it will remove the newline character.
buffer[pos] = 0;
Probably you have to implement that by yourself. Just keep reading character by character until you reached '\n', and return what you have read.
I didn't check, but I suspect that's how 'readline' is usually implemented.