I need to get argv[1] and argv[2] to different types. I found that I could only use sscanf() once or the next string in argv cannot be retrieved.
Here's my code.
int main( int argc, char *argv[])
{
char t;
float temp;
sscanf(argv[1], "-%[cf]",&t);
sscanf(argv[2], "%f", &temp);
return 0;
}
Only the first sscanf() can get the formatted value.
How could I also get done with argv[2]?
Attempt to save string data in a char leading to undefined behavior (UB).
"%[]" expects to match a character array.
// char t;
// sscanf(argv[1], "-%[cf]",&t);
char t[100];
if (sscanf(argv[1], "-%99[cf]",t) != 1) Handle_Failure();
Recommend:
Add the width limit, like 99, to limit string input. Set to 1 less than the size of t.
Check the return value of sscanf().
Related
int main(){
char *inputFile;
char *outputFile;
int numberOfBuffer;
int pageSize;
printf("Enter four inpus, separated by spaces: ");
scanf("%s %s B=%d P=%d", &inputFile,&outputFile,&numberOfBuffer,&pageSize);
readCSV(inputFile,outputFile,numberOfBuffer,pageSize);
return 0;
}
I want to read inputs and run readCSV() method with entering command line to
students.csv test.csv B=5 P=32
that line but my code does not work. Any help?
readCSV() input types
readCSV(char* fileName,char* outputFileName, int numberOfBuffer, int pageSize)
You invoked undefined behavior by passing data having wrong type to scanf(): %s expects char* (pointing at a valid buffer with enough length), but you passed char**.
You should allocate some arrays and pass pointers to them. Arrays in expressions (except for some exceptions) are automatically converted to pointers to its first elements, so you don't need explicit & for them.
Also you should specify the maximum length to read (at most the buffer size minus one for the terminating null-character) to avoid buffer overrun and check if scanf() succeeded to read all required things.
int main(){
char inputFile[1024];
char outputFile[1024];
int numberOfBuffer;
int pageSize;
printf("Enter four inpus, separated by spaces: ");
if(scanf("%1023s %1023s B=%d P=%d", inputFile,outputFile,&numberOfBuffer,&pageSize) != 4){
fputs("read error\n", stderr);
return 1;
}
readCSV(inputFile,outputFile,numberOfBuffer,pageSize);
return 0;
}
Both inputFile and outputFile need to be declared as arrays of char large enough to hold the expected inputs1:
#define MAX_FILE_NAME_LENGTH some-value
...
char inputFile[MAX_FILE_NAME_LENGTH+1]; // +1 for string terminator
char outputFile[MAX_FILE_NAME_LENGTH+1];
Then in the scanf call, do not use the unary & operator for inputFile and outputFile - when you pass an array expression as an argument, it automatically gets converted to a pointer to the first element of the array. You also want to check the result of scanf to make sure you got all your inputs:
if ( scanf( "%s %s B=%d P=%d", inputFile, outputFile, &numberOfBuffer, &pageSize ) != 4 )
{
// bad input somewhere, probably with numberOfBuffer or pageSize,
// handle as appropriate
}
else
{
// process input normally
}
But...
scanf is an awful tool for doing interactive input. It is very hard to make bulletproof, and for stuff like this you're really better off reading the whole thing as a single large string using fgets or something like that, then extracting data from that string.
One thing that may help simplify this for you is that you don't have to read the entire line in a single scanf call. You can read each element individually:
/**
* Start by reading the input file name; we use `fgets` instead
* of `scanf` because it's easier to protect against a buffer overflow
*/
if ( !fgets( inputFile, sizeof inputFile, stdin ) )
{
// error reading input file name, handle as appropriate
}
/**
* Successfully read inputFile, now read outputFile
*/
else if ( !fgets( outputFile, sizeof outputFile, stdin ) )
{
// error reading output file name, handle as appropriate
}
/**
* Now get the number of buffers - the leading blank in the format
* string tells scanf to skip over any leading whitespace, otherwise
* if you have more than one blank between the end of the output file
* name and the 'B' the read will fail.
*/
else if ( scanf( " B=%d", &numberOfBuffer ) != 1 )
{
// error getting number of buffers, handle as appropriate
}
/**
* And finally the page size, with the same leading blank space in the
* format string.
*/
else if ( scanf( " P=%d", &pageSize ) != 1 )
{
// error getting page size, handle as appropriate
}
else
{
// process all inputs normally.
}
Or the memory for them needs to be allocated dynamically, but as you're just learning C that's something to tackle later on.
Most of your issues are caused by misuse of scanf. The solution here is not to fix your usage of scanf, but to avoid it completely. (http://sekrit.de/webdocs/c/beginners-guide-away-from-scanf.html) Parameters like this should come from the command line arguments, not from the input stream. It is almost always better to leave the input stream clear so that it can be used for collecting data. (eg, write your program as a filter.) For example:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static void
check_prefix(const char *a, const char *prefix)
{
if( strncmp(a, prefix, strlen(prefix)) ){
fprintf(stderr, "Invalid argument: %s Must start with, %s\n",
a, prefix);
exit(EXIT_FAILURE);
}
}
static void
readCSV(const char *in, const char *out, int n, int p)
{
printf("in = %s, out = %s, n = %d, p = %d\n", in, out, n, p);
}
int
main(int argc, char **argv)
{
if( argc < 5 ){
fprintf(stderr, "Invalid number of arguments\n");
return EXIT_FAILURE;
}
check_prefix(argv[3], "B=");
check_prefix(argv[4], "P=");
char *inputFile = argv[1];
char *outputFile = argv[2];
int numberOfBuffer = strtol(argv[3] + 2, NULL, 10);
int pageSize = strtol(argv[4] + 2, NULL, 10);
readCSV(inputFile, outputFile, numberOfBuffer, pageSize);
return 0;
}
I'm trying to make a while loop that will be used to take an int and a double as input. But sometimes there could be a char in the input which I want to skip, and then continue the loop.
This will skip the loop and I want to still use the scanf:
scanf("%d%lf", &num,&n_double);
while((num != 'a')&&(n_double != 'a'))
First read the whole line, then use sscanf to parse the input line into an integer and a double. If, however parsing fails to give an int and a double, conclude that as the error case, i.e. a char present in the line.
You can try this code:
int main(int argc, char const *argv[])
{
char line[100];
while(fgets(line, 100, stdin)) {
int i;
double d;
if(sscanf(line, "%d %lf", &i, &d) == 2) {
printf("%d %lf\n", i, d);
}
else {
printf("wrong input!\n");
}
}
return 0;
}
Edited as per comment:
from cppreference.com
char *fgets( char *str, int count, FILE *stream );
Parameters
str - pointer to an element of a char array
count - maximum number of characters to write (typically the length
of str)
stream - file stream to read the data from
Return value
str on success, null pointer on failure.
So, while(fgets(str...)) translates to "while fgets doesn't return null", which translates to "while fgets continues to successfully read the input stream (which in this case is stdin, the standard input)".
Please take a look at the documentation for further clarification.
Do you mean input validation?
char line[80];
int ival;
double dval;
fgets(line, 80, stdin);
if (sscanf(line, "%d", &ival) == 1)
/* got an int */
else if (sscanf(line, "%f", &dval) == 1)
/* got a double */
else
fprintf(stderr, "Not int nor double\n");
I'm trying to make a function to validate mobile entry, the mobile number MUST starts with 0 and is 11 numbers (01281220427 for example.)
I want to make sure that the program gets the right entry.
This is my attempt:
#include <stdio.h>
#include <strings.h>
void integerValidation(char x[15]);
int main(int argc, char **argv)
{
char mobile[15];
integerValidation(mobile);
printf("%s\n\n\n", mobile);
return 0;
}
void integerValidation(char x[15]){
char input[15];
long int num = -1;
char *cp, ch;
int n;
printf("Please enter a valid mobile number:");
while(num<0){
cp = fgets(input, sizeof(input), stdin);
if (cp == input) {
n = sscanf(input, "%ld %c", &num, &ch);
if (n!=1) {printf("ERROR! Please enter a valid mobile number:");
num = -1;
}
else if (num<0)
printf("ERROR! Please enter a valid mobile number:");
else if ((strlen(input)-1)>11 || (strlen(input)-1)<11 || strncmp(&input[0], "0", 1) != 0){
printf("ERROR! Please enter a valid mobile number:");
num = -1;
}
}
}
long int i;
i = strlen(input);
//Because when I try to print it out it prints a line after number.
strcpy(&input[i-1], "");
strcpy(x, input);
}
Now, if I don't use
strcpy(&input[i-1], "");
the array prints a new line after the number, what would be a good fix other than mine? and how can I make this function optimized and shorter?
Thanks in advance!
Edit:
My question is: 1. Why does the input array prints a new line in the end?
2. How can I make this code shorter?
End of edit.
If you insist on using sscanf(), you should change the format this way:
int integerValidation(char x[15]) {
char input[15], c;
printf("Please enter a valid mobile number:");
while (fgets(input, sizeof(input), stdin)) {
if (sscanf(input, "%11[0123456789]%c", x, &c) == 2
&& x[0] == '0' && strlen(x) == 11 && c == '\n') {
// number stored in `x` is correct
return 1;
}
printf("ERROR! Please enter a valid mobile number:");
}
x[0] = '\0'; // no number was input, end of file reached
return 0;
}
%12[0123456789] parses at most 11 characters that must be digits.
%c reads the following character, which should be the trailing '\n'.
I verify that both formats have been matched, and the number starts with 0 (x[0] == '0') and it has exactly 11 digits.
You're seeing the newline, since fgets() reads until an EOF or a newline is received. The newline is stored in the buffer, and after that the string is terminated with '\0'.
An alternative would be to directly overwrite the newline with another null-byte: input[i-1] = '\0' (which basically does the same thing as your solution, but saves a function call).
The same goes for the check with strncmp with length 1, you can directly check input[0] == '0'. Note that you have to compare against '0' (char) here, not "0" (string).
A few other things I'm seeing:
You can also spare the %c in the format string for sscanf (you're never evaluating it anyway, since you're checking for 1 as return value), which also eliminates the need for char ch.
Also, you're passing char x[15] as argument to your function. This is a bit misleading, because what actually gets passed is a pointer to a char array (try using sizeof(x), your compiler will most likely issue a warning about the size of char * being returned by sizeof()).
What you could do is to ditch the char array input, which you're using as temporary buffer, and use the buffer which was handed over as argument. For this to be save, you should use a second funcion parameter to specify the size of the buffer which was handed to the function, which would result in a function header like as follows:
void integerValidation(char *input, size_t len);
With this, you'd have to use len instead of sizeof(input). The following question provides more detail why: C: differences between char pointer and array
Since you're not using a temporary buffer anymore, you can remove the final call to strcpy().
There are also a lot of checks for the number length/format. You can save a few:
If you use %lu instead of %ld no signed numbers are being converted, which saves you the check for num < 0.
You're checking whether the length of the read number is <11 or >11 - why not just check for !=11?
You're calling strlen() three times on the input-buffer (or still twice with the reworked check for lengh 11) - it makes sense to call it once, save the length in a variable and use that variable from then on, since you're not altering the string between the calls.
There is already an accepted answer, but for what it's worth, here is another.
I made several changes to your code, firstly avoiding "magic numbers" by defining the phone number length and an arbitrarily greater string length. Then there is no point passing an array x[15] to a function since it pays no regard to its length, might as well use the simpler *x pointer. Next, I return all reasons for failure back to the caller, that's simpler. And instead of trying to treat the phone number as a numeric entry (note: letters, spaces, hyphens, commas and # can sometimes be a part of phone number too) I stick to a character string. Another reason is that the required leading zero will vanish if you convert the entry to an int of some size. I remove the trailing newline that fgets() reads with the input line, and the result is this.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXLEN 11
#define STRLEN (MAXLEN+10)
int integerValidation(char *x);
int main(int argc, char **argv)
{
char mobile[STRLEN];
while (!integerValidation(mobile)) // keep trying
printf("Invalid phone number\n");
printf("%s\n\n\n", mobile); // result
return 0;
}
int integerValidation(char *x)
{
int i, len;
printf("Please enter a valid mobile number:");
if(fgets(x, STRLEN, stdin) == NULL) // check bad entry
return 0;
x [ strcspn(x, "\r\n") ] = 0; // remove trailing newline etc
if((len = strlen(x)) != MAXLEN) // check length
return 0;
if(x[0] != '0') // check leading 0
return 0;
for(i=1; i<len; i++) // check all other chars are numbers
if(!isdigit(x[i]))
return 0;
return 1; // success
}
Fairly new to C and am trying to parse input from a file. I have no problems getting the operation and address fields but I am getting the value "32767" for the size field.
Here is the code causing issues:
#include <stdio.h>
#include <string.h>
void read_file(char *filename)
{
// operation field, address field, size field
FILE *file = fopen(filename, "r");
char buff[25];
char operation;
long address;
int size;
char *cur_trace = fgets(buff, 25, file);
while (cur_trace) {
// read indivdual fields of trace
// if cur_trace[0] == I then ignore line
if (cur_trace[0] != 'I') {
sscanf(cur_trace, " %c %lx[^,]%*c%u", &operation, &address, &size);
printf("operation: %c\n address: %lx\n size: %u\n", operation, address, size);
}
cur_trace = fgets(buff, 25, file);
}
}
int main(int argc, char const *argv[])
{
read_file("tester.txt");
return 0;
}
and here is the input text I am reading. All lines beginning with 'I' are being ignored.
I 0400d7d4,8
M 0421c7f0,4
L 04f6b868,8
S 7ff0005c8,8
The brackets is not a generic part of the format string, it's part of a specific scanf format code to read strings. It can't just be placed anywhere as a sort of pattern, or used for any other format.
And by the way, reading hexadecimal values will stop at the first non-hexadecimal character, so you don't need it anyway. Just doing e.g.
sscanf(cur_trace, " %c %lx,%u", &operation, &address, &size);
should be enough (if the types of the variables are correct).
The problem is that your format string is not parsing the 3rd argument &size, because of the format string.
The 32767 value is just uninitialized junk.
You need to check that sscanf returns 3 so that all arguments are accounted for.
Assuming I have a variable:
char variableName[256];
How can i store input from the command line if i'm given a list of numbers like 10 11 12 .. etc. I'm using
fgets(variableName, sizeof(variableName), stdin)
and its turning it directly into (1,0,1,1, etc) as opposed to the converting between the spaces to (10, 11, 12, etc).
Should I perhaps use scanf, and store based of white space; Just unsure of the syntax/what is common?
Thanks guys!
if you know how many numbers will be inputed then use sscanf after using fgets or use scanf directly (not recommended)
int var[NO_OF_INPUTS];
fgets(variableName , sizeof variableName , stdin);
sscanf(variableName , "%d %d %d ... %d" , &var[0] , &var[1] , ... , &var[NO_OF_INPUTS - 1]);
if you don't know the number of inputs then you can make a function that counts how many numbers were entered by counting the number of whitespace after fgets then maybe use strtok to jump between whitespace and strtol to convert that into a number
If you are getting values from command line then you can simply do the job by using argc, argv and strtol. No need to create new variables!
argc contains the number of command line argument passed to your program.
argv is the array of arguments.
I hope this code will help:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int i;
char *p;
long int li_arr[argc];
if (argc==1)
{
puts("No arguments provided");
exit(1);
}
for(i=1;i<argc;i++)
{
li_arr[i] = strtol(argv[i],&p,10);
printf("%ld\n",li_arr[i]);
}
return 0;
}
You don't have to call fgets or any other function because arguments passed to program are in argv[] array. You can acces them directly just with
int i = atoi( argv[1]);
but better you should use sscanf which provides error checking:
if ( sscanf ( argv[1], "%d", &i) != 1) { printf ( "error - not an integer"); }
or strtol() which prevents under/overflow:
char *endptr, *str;
str = argv[1];
long val = strtol( str, &endptr, 10);
http://linux.die.net/man/3/sscanf