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;
}
Related
Fscanf does not work and I can't understand why. It only reads strings and nothing else.
1 2 3 is written in the file.txt.
Here's the code:
include<stdio.h>
int main()
{
FILE* ptr = fopen("file.txt","r");
if (ptr==NULL)
{
printf("no such file.");
return 0;
}
char* buf[100];
int a;
fscanf(ptr," %d ",a);
printf("%d\n", a);
fscanf(ptr," %s ",buf);
printf("%s\n", buf);
return 0;
}
There are several issues in your provided code, I would like to first talk about before getting to the answer you have asked for.
1.
fscanf(ptr," %d ",a);
This is false. Here the address of an int is needed as third argument. You need the ampersand operator & to access an address of a variable, like:
fscanf(ptr," %d ",&a);
2.
fscanf(ptr," %s ",buf);
Is also false. A pointer to a char array is needed here as third argument, but buf is declared as an array of 100 pointers after
char* buf[100];
You need to declare buf in the right way as a char array, like:
char buf[100];
to make it work with:
fscanf(ptr," %s ",buf);
3.
You have forgot the # in the include directive for stdio.h:
include<stdio.h>
Also, there should be a white space gap between #include and the file you want to include.
So the preprocessor directive should be look like:
#include <stdio.h>
4.
If the open stream operation fails you should not use return with a value of 0.
If an operation fails, that is crucial to the program itself, the return value of the program should be a non-zero value (the value of 1 is the most common) or EXIT_FAILURE(which is a macro designed for that purpose (defined in header <stdlib.h>)), not 0, which indicating to outer software applications as well as the operation system, that a problem has occurred and the program could not been executed successfully as it was ment for its original purpose.
So use:
if (ptr==NULL)
{
printf("no such file.");
return 1;
}
5.
Fscanf does not work and I can't understand why. It only reads strings and nothing else.
What did you expect as result? What do you want that fscanf()should do?
The format specifier %s is used to read strings until the first occurrence of a white space character in the input stream (skips leading white space characters until the matching sequence is encountered), pointed to by ptr.
Then I get from your header title:
I have problems with getting numbers from the file
that you want only the numbers from the file.
If you want to get the numbers only from the text file, you do not need the char array buf and the whole things with reading strings at all.
Simply use something like:
int a,b,c; // buffer integers.
fscanf(ptr,"%d %d %d",&a,&b,&c);
printf("%d %d %d\n",a,b,c);
Of course, this expressions implying that it only work with the given example of the 1 2 3 data or anything equivalent to (integer) (integer) (integer) but I think you get the idea of how it works.
And, of course, you can apply the scan operation by using fscanf() (and also the print operation by using printf()) for each integer separate in a loop, instead to scan/print all integers with just one call to fscanf() and printf(), f.e. like:
#define Integers_in_File 3
int array[Integers_in_File];
for(int i = 0; i < Integers_in_File; i++)
{
fscanf(ptr,"%d",&array[i]); // writing to respective int buffers,
} // provided as elements of an int array.
for(int i = 0; i < Integers_in_File; i++)
{
printf("%d",array[i]); // Output the scanned integers from
} // the file.
The whole program would be than either:
#include <stdio.h>
int main()
{
FILE* ptr = fopen("file.txt","r");
if (ptr==NULL)
{
printf("no such file.");
return 1;
}
int a,b,c; // buffer integers.
fscanf(ptr,"%d %d %d",&a,&b,&c);
printf("%d %d %d\n",a,b,c);
return 0;
}
Or that:
#include <stdio.h>
#define Integers_in_File 3
int main()
{
int array[Integers_in_File];
FILE* ptr = fopen("file.txt","r");
if (ptr==NULL)
{
printf("no such file.");
return 1;
}
for(int i = 0; i < Integers_in_File; i++)
{
fscanf(ptr," %d",&array[i]); // writing to respective intbuffers,
} // provided as elements of an int
// array.
for(int i = 0; i < Integers_in_File; i++)
{
printf("%d",array[i]); // Output the scanned integers from
} // the file.
return 0;
}
The variadic arguments to fscanf() must be pointers.
Whitespace is the default delimiter and need not be included in the format string.
If the input stream does not match the format specifier, the content remains buffered, and the argument is not assigned. You should therefore check the conversion which can fail due to mismatching content or EOF.
To declare an array for a character string, the array type must be char not char* - that would be an array of pointers, not an array of characters.
char buf[100];
int a;
if( fscanf( ptr, "%d", &a ) > 0 )
{
printf( "%d\n", a ) ;
if( fscanf(ptr, "%s", buf) > 0 )
{
printf( "%s\n", buf ) ;
}
}
Or simply:
char buf[100];
int a;
if( fscanf( ptr, "%d%s", &a, buff ) == 2 )
{
printf( "%d\n", a ) ;
printf( "%s\n", buf ) ;
}
I have to complete this assignment and I'm struggling with it.
"Write a program, called sort_file, which takes a text file as input and produces an output file which has all the original lines in alphabetical order.
The program should do the following:
Prompt the user for the names of the input and output files. You can prepare an input file with appropriate unsorted data. A simple file with one word per line will suffice.
Declare and define three functions to perform the following tasks:
Read the data from the input file into an array.
Sort the array using the bubble sort algorithm.
Write the sorted data array to the output file."
I have created a .txt file for this program to sort but I'm stuck and just getting more confused the more i look at it. This is what I've gotten done so far
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZEA 50
#define SIZEB 20
void readFile(char a[]);
void sortFile(char*s[50],int n);
void writeFile();
int main( void ) {
char name[20];
printf("PLease enter filename:");
scanf("%s", &*name);
readFile(&*name);
printf("\n");
sortFile(name);
printf("\n");
writeFile();
}
void readFile(char a[]) {
FILE *cfPtr = NULL;
char list[SIZEA][SIZEB];
int i = 0;
int j = 0;
cfPtr = fopen("unsorted.txt", "r");
while (fgets(list[i], SIZEA, cfPtr)) {
list[i][strlen(list[i]) - 1] = '\0';
i++;
}
j = i;
for (i = 0; i < j; i++) {
printf("%s\n", list[i]);
}
}
void sortfile(char*s[50], int n) {
int i;
int j;
int compare;
char temp[1][10];
for (i = 0; i < n; i++)
for (j = 0; j < n - 1; j++)
{
compare = strcmp(s[j], s[j+1]);
if (compare > 0)
{
strcpy(temp[0], s[j+1]);
strcpy(s[j+1], s[j]);
strcpy(s[j], temp[0]);
}
}
}
void writeFile() {
FILE *cfPtr = fopen("unsorted.txt", "w");
fwrite(list, sizeof(char), sizeof(list), cfPtr);
fclose(cfPtr);
}
But i'm not sure what to fill for arguments for the functions or hot to get the main body of the program running. I'm a novice and I'm finding this topic hard to understand.
regarding this kind of line:
scanf("%s", &*name);
In C, an array name degrades to the address of the first byte of the array. (there are a couple of exceptions, but they do not pertain here) so the line should be:
scanf("%s", name);
However, this has a couple of problems.
the format specifier '%s' will allow the user to overrun the input buffer. the %s format specifier needs a max length modifier (and remember that a NUL byte will be automatically appended when using the %s format specifier. Suggest:
scanf("%*s", sizeof( name ) -1, name);
when compiling, (strongly suggest performing compile and link as separate steps) always enable all the warnings, then fix those warnings. (for gcc, at a minimum use: -Wall -Wextra -pedantic )
when prototype'ing a function that has no parameters, do not use:
void writeFile();
as that is telling the compiler there will be parameters, but they are not being defined yet. Rather use:
void writeFile( void );
as that tells the compiler there will be no parameters.
Note: the actual function declaration can/should not have the 'void' between the parens
when declaring variables and/or parameters use meaningful names. a parameter name like char 'a[]' is (mostly) meaningless. Rather use something like:
char * filename
or
char filename[]
when #define'ing numeric values, strongly suggest surrounding the numeric value with parens to avoid certain 'text replacement' errors. so this:
#define SIZEA 50
would be much better written as
#define SIZEA (50)
to check the returned value from fopen() to assure the operation was successful:
FILE *fp = NULL:
...
if( NULL == ( fp = fopen( filename, "r" ) )
{ // then error occurred
perror( "fopen failed" );
exit( EXIT_FAILURE );
}
which will write to stderr with the 'fopen failed' text plus the related system error message
This line:
list[i][strlen(list[i]) - 1] = '\0';
will not necessarily eliminate a trailing newline.
scenario 1: end of file reached and no trailing newline in file. scenario 2: on DOS/Windows a newline is 2 characters, not one.
A correct method (there are several others) would be:
char *newline == NULL;
if( NULL != (newline = strstr( list[i], "\n" ) ) )
{
*newline = '\0';
}
regarding this line:
while (fgets(list[i], SIZEA, cfPtr))
1. if the input file contains more than SIZEA entries,
then the array list[] will be overrun,
resulting in undefined behaviour and can lead to a seg fault event.
2. the size of each entry in the array is SIZEB.
Therefore suggest using:
while ( i<SIZEA && fgets(list[i], SIZEB, cfPtr))
A more robust method would be: char **list = NULL; Then use realloc() to update the number of entries in the list and use readline() or getline() to input/allocate the memory for each entry in the list.
be sure to pass each of the resulting pointers to free() to avoid any memory leak,
regarding the sortfile() function:
this line has some problems:
char temp[1][10];
1. an entry can be up to 20 characters line (SIZEB)
so 10 is inflexible and too short.
2. does not need to be a 2D array. Suggest:
char temp[SIZEB];
...
strcpy(temp, s[j+1]);
strcpy(s[j+1], s[j]);
strcpy(s[j], temp);
the function: writefile():
fails to implement the problem scenario that the output file name is gotten by prompting the user for the name. rather it is using some hard coded name: unsorted.txt. That file name is misleading as the output is sorted, not unsorted.
regarding this line in writefile():
fwrite(list, sizeof(char), sizeof(list), cfPtr);
in general, the best practice is the third parameter be 1, to make it easy to perform error checking.
the expression: sizeof(char) is defined in the standard as 1.
Suggest swapping the second and third parameters.
However, there are no <newline> characters in list[] and there is LOTS of uninitialized characters on most rows in the list[] array. so the output would be junk. Suggest: output each line separately by :
char outBuf[SIZEB+3]; // +3 allows for newline and NUL byte
for( int i = 0; i < SIZEA; i++)
{
int bytecount = sprintf( outBuf, "%s\n", list[i] );
fwrite( outBuf, bytecount, 1, cfPtr );
}
Note: using SIZEA in the for() loop assumes that the list[] array use all the entries, I.E. that the input file contains exactly 50 lines. That may not be true, so the code should keep a count of the number of lines read in the readfile() function and use that number in the sortfile() and writefile() functions.
regarding SIZEA and SIZEB.
those names are rather meaningless. Suggest:
#define MAX_ROWS (50)
#define MAX_COLUMNS (20)
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
In my program I am taking user input and parsing it into a 2d char array. The array is declared as:
char parsedText[10][255] = {{""},{""},{""},{""},{""},
{""},{""},{""},{""},{""}};
and I am using fgets to grab the user input and parsing it with sscanf. This all works as I think it should.
After this I want to pass parsedText into execvp, parsedText[0] should contain the path and if any arguments are supplied then they should be in parsedText[1] thru parsedText[10].
What is wrong with execvp(parsedText[0], parsedText[1])?
One thing probably worth mentioning is that if I only supply a command such as "ls" without any arguments it appears to work just fine.
Here is my code:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "308shell.h"
int main( int argc, char *argv[] )
{
char prompt[40] = "308sh";
char text[40] = "";
char parsedText[10][40] = {{""},{""},{""},{""},{""},
{""},{""},{""},{""},{""}};
// Check for arguments to change the prompt.
if(argc >= 3){
if(!(strcmp(argv[1], "-p"))){
strcpy(prompt, argv[2]);
}
}
strcat(prompt, "> ");
while(1){
// Display the prompt.
fputs(prompt, stdout);
fflush(stdout);
// Grab user input and parse it into parsedText.
mygetline(text, sizeof text);
parseInput(text, parsedText);
// Check if the user wants to exit.
if(!(strcmp(parsedText[0], "exit"))){
break;
}
execvp(parsedText[0], parsedText[1]);
printf("%s\n%s\n", parsedText[0], parsedText[1]);
}
return 0;
}
char *mygetline(char *line, int size)
{
if ( fgets(line, size, stdin) )
{
char *newline = strchr(line, '\n'); /* check for trailing '\n' */
if ( newline )
{
*newline = '\0'; /* overwrite the '\n' with a terminating null */
}
}
return line;
}
char *parseInput(char *text, char parsedText[][40]){
char *ptr = text;
char field [ 40 ];
int n;
int count = 0;
while (*ptr != '\0') {
int items_read = sscanf(ptr, "%s%n", field, &n);
strcpy(parsedText[count++], field);
field[0]='\0';
if (items_read == 1)
ptr += n; /* advance the pointer by the number of characters read */
if ( *ptr != ' ' ) {
strcpy(parsedText[count], field);
break; /* didn't find an expected delimiter, done? */
}
++ptr; /* skip the delimiter */
}
}
execvp takes a pointer to a pointer (char **), not a pointer to an array. It's supposed to be a pointer to the first element of an array of char * pointers, terminated by a null pointer.
Edit: Here's one (not very good) way to make an array of pointers suitable for execvp:
char argbuf[10][256] = {{0}};
char *args[10] = { argbuf[0], argbuf[1], argbuf[2], /* ... */ };
Of course in the real world your arguments probably come from a command line string the user entered, and they probably have at least one character (e.g. a space) between them, so a much better approach would be to either modify the original string in-place, or make a duplicate of it and then modify the duplicate, adding null terminators after each argument and setting up args[i] to point to the right offset into the string.
You could instead do a lot of dynamic allocation (malloc) every step of the way, but then you have to write code to handle every possible point of failure. :-)