An efficient way of reading integers from file - c

I'd like to read all integers from a file into the one list/array. All numbers are separated by space (one or more) or end line character (one or more). What is the most efficient and/or elegant way of doing this? Functions from c++ library are prohibited.
I did it this way:
/* We assume that 'buffer' is big enaugh */
int i = 0;
while(fscanf(fp, "%d", &buffer[i]) != EOF) {
++i;
}
Sample data:
1 2 3
4 56
789
9 91 56
10
11

OP's code is close. Test against 1 rather than EOF so code does not get caught in a endless loop should non-numeric data occur. I'd use a for() loop, but while is OK too.
Note that "%d" directs fscanf() to first scan and discard any white-space including ' ' and '\n' before looking for sign and digits.
#define N 100
int buffer[N];
int i,j;
for (i=0; i<N; i++) {
if (fscanf(fp, "%d", &buffer[i]) != 1) {
break;
}
}
for (j=0; j<i; j++) {
printf("buffer[%d] --> %d\n", j, buffer[j]);
}

You can use fgets to read each line from the file into a string, say char *line .
Then you can loop through that string char by char and use isDigit to determine if the character is a digit or not.
To read numbers with more than one digit place each digit in a string and use atoi to convert them to an integer

Related

String Input with multiple lines

int main() {
char userInput[100]; //Store user input
//Take user input
//scanf(" %s",&userInput);
//scanf("%[^\n]s",&userInput);
//scanf("%[^\n]", &userInput);
//gets(userInput);
scanf("%[]s", &userInput); //This takes input but doesnt leave input loop
printf(" %s",userInput);
//i = index to start for looping through the string, starting at the beginning
//count = Stores occurrences of '$'
//inputLength = length of the input, used for limit of loop
int i =0,count =0;
int inputLength = strlen(userInput);
//Loop through the user input, if the character is '$', the integer count will be incremented
for (i; i < inputLength; i++){
if (userInput[i] == '$'){
count++;
}
}
printf("%d", count);
return 0;
}
Hi i'm having some issues with my code, i need to take an input of 3 lines and count the number of'$' in the input. The input method not commented "scanf("%[]s", &userInput);" is the one only i have discovered to take all 3 lines of input, BUT i can't break the input loop and continue with my program.
Any help would be greatly appreciateed
To read 3 lines with the cumbersome scanf(), code needs to look for '$', '\n', and EOF. The rest of input is discardable.
int count = 0;
int line = 0;
while (line < 3) {
scanf("%*[^$\n]"); // Scan for any amount of characters that are not $ nor \n,
// "*" implies - do not save.
char ch;
if (scanf("%c", &ch) != 1) { // Read next character.
break;
}
if (ch == '$') count++;
else line++;
}
printf("$ count %d\n", count);
As #chux suggested, reading with fgets provides a convenient way to protect from buffer overrun and without having to hard code field-width modifiers in scanf conversion specifiers.
Here, if all you need to do is count the number of '$' characters found in your input (regardless of how many lines), you can simply read ALL the input in fixed sized chunks of data. fgets does just that. It doesn't matter if you have one line, or one million lines of input. It also doesn't matter if your input lines are one-character or one million characters long. You can simply read each line and count the number of '$' found within each chunks of data read, keeping a count of the total found.
You can do this for any character. If you wanted to also count the number of line, you can simply check for '\n' characters and keep a total there as well. The only corner-case in counting lines with fgets is to insure you protect against a non-POSIX end-of-file (meaning a file with no '\n' as the final character). There are a couple of ways to handle this. Checking that the last character read was a '\n' is as good as any.
Putting the pieces together, and protecting against a non-POSIX eof, you could do something similar to the following, which simply reads all data available on stdin and outputs a final '$' and line count:
#include <stdio.h>
#define MAXC 100
int main (void) {
char buf[MAXC] = ""; /* buffer to hold input in up to MAXC size chunks */
size_t lines = 0, dollars = 0; /* counters for lines and dollar chars */
int i = 0;
while (fgets (buf, MAXC, stdin)) /* read all data */
for (i = 0; buf[i]; i++) /* check each char in buf */
if (buf[i] == '$') /* if '$' found */
dollars++; /* increment dollars count */
else if (buf[i] == '\n') /* if '\n' found */
lines++; /* increment line count */
if (i && buf[i-1] != '\n') /* protect against non-POSIX eof */
lines++;
/* output results */
printf ("input contained %zu lines and %zu '$' characters.\n",
lines, dollars);
return 0;
}
Look things over and let me know if you have further questions.
scanf("%[]s", &userInput);" is the one only i have discovered to take all 3 lines of input, BUT i can't break the input loop and continue with my program.
"%[]" is an invalid scanf() specifier. Anything may happen, it is undefined behavior, including taking all lines in and not returning.
The 's' in the format serves no purpose here - drop it.
Yes fgets() is best but let us abuse scanf() to read 3 lines and look for '$'.
char line[3][100] = {0};
// v--------- Consume all leading whitespace
// | vv ----- limit input to 99 characters as scan() appends a \0
// | || v-v-- Look for "not \n"
#define FMT_1LINE " %99[^\n]"
// Let the compiler concatenate the 3 formats into 1 string for scanf
int scan_count = scanf(FMT_1LINE FMT_1LINE FMT_1LINE, line[0], line[1], line[2]);
// Check return value
if (scan_count == 3) {
// Successfully read 3 lines
int count = 0;
for (int line_index = 0; line_index < 3; line_index++) {
char *s = line[line_index];
while (*s) { // no need for strlen(), just loop until the null character
count += *s == '$';
s++;
}
}
printf("$ count %d\n", count);
}
You write:
scanf("%[]s", &userInput); //This takes input but doesnt leave input loop
but the comment is at best misleading. Your format string is malformed, so the behavior of the scanf call is undefined. An empty scan set (between the [] in the format) does not make sense, because the resulting field could never match anything. Therefore, a ] appearing immediately after the opening ] of the scan set is interpreted as a literal character not the ending delimiter. Your scan set is therefore unterminated.
Note, too, that %[ is its own field type, separate from %s. An 's' following the closing ] of the scan set is not part of such a field descriptor, but rather an ordinary character to match.
A trivial way to do this with scanf would be to read characters one at a time in a loop via a %c field. This is probably not what the exercise is looking for, and it's a hack to use scanf() instead of getchar() for this purpose, but perhaps it would serve:
int nl_count = 0;
int dollar_count = 0;
do {
char c;
int result = scanf("%c", &c);
if (result != 1) {
break;
}
switch (c) {
case '\n':
nl_count++;
break;
case '$':
dollar_count++;
break;
}
} while (nl_count < 3);
I'm afraid it would be much more complicated to do it safely reading multiple characters at a time with a %[ field, and there is no safe way to read all three lines in one scanf call, unless you can rely on the input lines not to exceed a line length limit known to you.
int readMatrix() {
char userInput[100][3]; //Store user input
int j = 0, m = 0;
for(m = 0; m < 3; m++){
scanf("%s", &userInput[j][m]); //This takes input (Ex: 22 *(enter)* 33$ *(enter)* 66$ *(enter)*
j++; //increase the column
}
int i =0,count =0;
m = 0;
//Loop through the user input, if the character is '$', the integer count will be incremented
for (i = 0; i < 100; i++){
for(m = 0; m < 3; m++){
if (userInput[i][m] == '$'){
count++;
}
}
}
printf("%d", count);
return 0;
}

Scanning values in C till hit a new-line char, '\n'

How can I scanf() the integer values I enter into an array until I hit enter.
I believe I can use getchar() != '\n'.
but how do I loop through the line ?
Suppose my input is 20 21 2 12 2. I want an array that has all those inputs.
What given functions could I use in order to scan them all in.
You are trying to read integers as characters so once read you need to convert it to integers.
Read the line to a buffer using fgets() then parse the input buffer to get integers.
Store the integers to the array.
The code looks like
char buf[300];
int a[5],i=0;
fgets(buf,sizeof(buf),stdin);
char *p = strtok(buf," ");
while(p != NULL)
{
char *endptr;
a[i] = strtol(p,&endptr,10);
if ((*endptr != '\0') && (isspace(*endptr) == 0))
printf("warning: invalid value detected\n");
else
i++;
p = strtok(NULL," ");
}
You can use the alternative strtol() instead of atoi() to convert string to integer.
PS: Your buf should be large enough to hold the whole line. fgets() read till newline character.
If you use getchar() you obtain digits one by one, so you need
to store them first in the buffer, and when white space comes,
you convert those digits into a number, and store it into array.
here is the explanation of the code I made for you.
1st if statement : if obtained character is a digit, store it in buf
2nd if statement : if obtained character is a white space or EOL and at least 1 digit is stored in buf, convert digits into number and store it in array a.
3rd if statement : if obtained character is not a digit or a white space or a EOL, warns users.
4th if statement : if obtained character is a EOL, end loop.
The code below works fine.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main(){
#define BUFSIZE 50
#define ARRAYSIZE 5
int i,k,a[ARRAYSIZE];
char c,buf[BUFSIZE];
for(i=0,k=0;(i<BUFSIZE)&&(k<ARRAYSIZE);){
c=getchar();
if(isdigit(c)){
buf[i++] = c;
}else if((i>0) && (c==' ' || c=='\n')){
buf[i] = '\0';
a[k++] = atoi(buf);
i=0;
}else if(!(c==' ' || c=='\n')){
printf("warning : invalid value %c is detected\n",c);
i=0;
}
if(c=='\n'){
break;
}
}
printf("input :");
for(i=0;i<ARRAYSIZE;i++){
printf("%d, ",a[i]);
}
printf("\n");
}

Reading unsigned char from .txt file using fread give different values

I have the following code snipped to read 16 unsigned char values from a .txt file.
#include<stdio.h>
#include<stdlib.h>
int main()
{
int i, j, k, load_size;
unsigned char *buf;
load_size = 16;
buf = (unsigned char *)malloc(load_size);
FILE *fin;
fin = fopen("demo_input1.txt", "r");
fread(buf, 1, load_size, fin);
for(i=0;i<16;i++){
printf("%d ", *buf);
buf++;
}
system("PAUSE");
}
The file 'demo_input1.txt' contains the values 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16. But I am getting output in the console values 49 32 50 32 51 32 52 32 53 32 54 32 55 32 56 32. Can anybody help me by telling me what is going wrong here? Thanks
fread is for reading raw inputs. As your file is formatted text, use the following:
int nums[SIZE];
int count = 0;
while( fscanf(fin, "%d", nums + count) == 1 ) {
if(++count == SIZE) break; /* More than expected numbers in file */
}
Later you can print using:
for(i = 0; i < count; i++) {
printf("%d ", nums[i]);
}
fscanf is one way to read formatted input from files. You can use malloc, walking pointers as shown in your original code snippet or as per your requirements.
What is going on is completly correct, yet not what you expected. So, whats going on? You read 1 byte from the file, into a char, then you output it using %d, which will output as decimal. Have a look at an ascii table, if you read the char 1, its ASCII value is 49, 32 is space, 50 is 2, and so on. You cannot just read plain numbers like that, your code, replacing the %d with %c would only work on binary files, but not on human readable files.
What you want to use instead of fread is fscanf, which works like scanf but reads from a file. There you can specify to read an integer, thus getting the whole numbers without the spaces. These you can check if they are smaller than 256, if yes, cast to char.
The other way of how this can be done is using fgets() to read the whole line to the buffer break the line using space as delimiter and later convert string to integers and store them or print them.
char buf[300];
int a[30],i=0;
while(fgets(buf,sizeof(buf),fin))
{
char *p = strtok(buf," ");
i = 0;
while( p != NULL)
{
a[i] = atoi(p);
printf("%d ",a[i]);
i++;
p = strtok(NULL," ");
}
}
PS: When fgets() is used you need to have predefined large array to hold your input else it may lead to erroneous results.
It is considering the ascii value of space as 32(ASCII for space).
Just make one simple change in the for loop.
Instead of %d use %c
for(i=0;i<16;i++)
{
printf("%c ", *buf);
buf++;
}

How to limit input length with scanf

In this program I have taken a dimensional character array of size[3][4],
as long as I enter a 3 characters for each row it will work well.
For example: if I enter abc abd abd I get the same output but if i enter more letters in the first or second or 3rd row I get an error.
How should I check for null character in 2 dimensional?
# include <stdio.h>
#include <conio.h>
# include <ctype.h>
void main()
{
int i=0;
char name[3][4];
printf("\n enter the names \n");
for(i=0;i<3;i++)
{
scanf( "%s",name[i]);
}
printf( "you entered these names\n");
for(i=0;i<3;i++)
{
printf( "%s\n",name[i]);
}
getch();
}
As pointed out by #SouravGhosh, you can limit your scanf with "%3s", but the problem is still there if you don't flush stdin on each iteration.
You can do this:
printf("\n enter the names \n");
for(i = 0; i < 3; i++) {
int c;
scanf("%3s", name[i]);
while ((c = fgetc(stdin)) != '\n' && c != EOF); /* Flush stdin */
}
How should I chk for null character in 2 dimensional ... [something has eaten the rest part, I guess]
You don't need to, at least not in current context.
The problem is in your approach of allocating memory and putting input into it. Your code has
char name[3][4];
if you enter more that three chars, you'll be overwriting the boundary of allocated memory [considering the space of \0]. You've to limit your scanf() using
scanf("%3s",name[i]);
Note:
change void main() to int main(). add a return 0 at the end.
always check the return value of scanf() to ensure proper input.
EDIT:
As for the logical part, you need to eat up the remainings of the input words to start scanning from the beginning of the next word.
Check the below code [Under Linux, so removed conio.h and getch()]
# include <stdio.h>
# include <ctype.h>
int main()
{
int i=0; char name[3][4];
int c = 0;
printf("\n enter the names \n");
for(i=0;i < 3;i++)
{
scanf( "%3s",name[i]);
while(1) // loop to eat up the rest of unwanted input
{ // upto a ' ' or `\n` or `EOF`, whichever is earlier
c = getchar();
if (c == ' ' || c == '\n' || c == EOF) break;
}
}
printf( "you entered these names\n");
for(i=0;i<3;i++)
{
printf( "%s\n",name[i]);
}
return 0;
}
(Cringing after reading the answers to date.)
First, state the problem clearly. You want to read a line from stdin, and extract three short whitespace separated strings. The stored strings are NUL terminated and at most three characters (excluding the NUL).
#include <stdio.h>
void main(int, char**) {
char name[3][4];
printf("\n enter the names \n");
{
// Read tbe line of input text.
char line[80];
if (0 == fgets(line, sizeof(line), stdin)) {
printf("Nothing read!\n");
return 1;
}
int n_line = strlen(line);
if ('\n' != line[n_line - 1]) {
printf("Input too long!\n");
return 2;
}
// Parse out the three values.
int v = sscanf(line, "%3s %3s %3s", name[0], name[1], name[2]);
if (3 != v) {
printf("Too few values!\n");
return 3;
}
}
// We now have the three values, with errors checked.
printf("you entered these names\n%s\n%s\n%s\n",
name[0], name[1], name[2]
);
return 0;
}
you might consider something on the order of scanf( "%3s%*s",name[i]);
which should, if I recall correctly, take the first three characters (up to a whitespace) into name, and then ignore anything else up to the next white space. This will cover your long entries and it does not care what the white space is.
This is not a perfect answer as it will probably eat the middle entry of A B C if single or double character entries are mode. strtok, will separate a line into useful bits and you can then take substrings of the bits into your name[] fields.
Perhaps figuring out the entire requirement before writing code would be the first step in the process.

How do I combine two strings and then convert it to an integer in C

I am reading a data file with numbers in it and extracting some of the numbers and converting them into one integer.
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
FILE* fp = fopen("data.txt", "r");
int i;
char data[5];
for(i = 0; i < 5; i++)
{
data[i] = fgetc(fp);
}
fclose(fp);
}
I have an array of dates in a char matrix and want to have separate arrays for the date month and year. How do I take any n numbers from the data file and turn them into an n digit integer? I have tried strcat followed by atoi but my lack of understanding of pointers gets in the way and I constantly get error messages?
I/O:
Input data (bank statement):
09,08,2014,"BOOK SHOP",12.34,5.67,
10,08,2014,"CAR MECHANIC",52.44,5.67,
11,08,2014,"CHIP SHOP",67.34,5.67,
The desired output is separate arrays for date, month, year, place of purchase, amount and balance. I have separate arrays for each but the numbers are strings and not integers. How can I convert each column to an integer array?
If you NULL terminate your character array, then it is effectively a string representation of a number as far as C is concerned. You can then just use atoi() directly on it:
int data_int;
char data_str[N+1];
for (i = 0; i < N; i++)
{
data_str[i] = fgetc(fp);
/* to be really sure, you should make sure each character
* you read is a decimal digit from '0' - '9'
*/
}
data_str[N] = '\0';
data_int = atoi(data_str);
I'm assuming you are interested in reading the lines of data like:
09,08,2014,"BOOK SHOP",12.34,5.67,
10,08,2014,"CAR MECHANIC",52.44,5.67,
11,08,2014,"CHIP SHOP",67.34,5.67,
It looks like you can use fscanf(), but you need to be careful:
int day[20];
int month[20];
int year[20];
char name[20][15];
double amount[20];
double balance[20];
for (int i = 0; i < 20; i++)
{
if (fscanf(fp, "%d ,%d ,%d , \" %14[^\"] \" ,%lf ,%lf ,",
&day[i], &month[i], &year[i], name[i],
&amount[i], &balance[i]) != 6)
break;
}
You might not need every one of the spaces, and you'll never know whether the final comma on the line was missing. Note that the code avoids buffer overflows, both by limiting the number of iterations on the loop and by limiting the length of the string field. I chose 15 for the length of the names simply so that it is clear which number is the number of entries and which is the length of each entry.
You might prefer the fgets() and sscanf() approach:
int day[20];
int month[20];
int year[20];
char name[20][15];
double amount[20];
double balance[20];
char buffer[4096];
int i; // Outside loop so it can be accessed after the loop
for (i = 0; i < 20 && fgets(buffer, sizeof(buffer), fp) != 0; i++)
{
if (sscanf(buffer, "%d ,%d ,%d , \" %14[^\"] \" ,%lf ,%lf ,",
&day[i], &month[i], &year[i], name[i],
&amount[i], &balance[i]) != 6)
break;
}
This will reject lines which don't match, whereas the original code using fscanf() directly would work with data like this:
09,08,2014,"BOOK SHOP",12.34,5.67,10,08,2014,"CAR MECHANIC",
52.44,5.67,11,
08,
2014,
"CHIP SHOP",
67.34,
5.67,
The code shown more or less replaces the lines in the question from char data[5]; through the end of the loop. You can add code to print the values read.
for (int j = 0; j < i; j++)
printf("%.4d-%.2d-%.2d %-14s %8.2f %8.2f\n",
year[j], month[j], day[j], name[j], amount[j], balance[j]);

Resources