How would I go about making an array of file pointers in C?
I would like to create an array of file pointers to the arguments of main... like a1.txt, a2.txt, etc... So I would run ./prog arg1.txt arg2.txt arg3.txtto have the program use these files.
Then the argument for main is char **argv
From argv, I would like to create the array of files/file pointers. This is what I have so far.
FILE *inputFiles[argc - 1];
int i;
for (i = 1; i < argc; i++)
inputFiles[i] = fopen(argv[i], "r");
The code is fine, but remember to compile in C99.
If you don't use C99, you need to create the array on heap, like:
FILE** inputFiles = malloc(sizeof(FILE*) * (argc-1));
// operations...
free(inputFiles);
#include <stdio.h>`
int main(int argc, char **argv)
{
FILE *inputFiles[argc - 1];
int i;
for (i = 1; i < argc; i++)
{
printf("%s\n",argv[i]);
inputFiles[i] = fopen(argv[i], "r");
printf("%p\n",inputFiles[i]);
}
return 0;
}
It prints different pointers for each file pointer along with the names. Allowing OS to close files properly :)
Related
I have a C program that takes arguments from the command line. Prints the arguments in reverse order. And finds the needle/substring in the haystack. I have the following code:
Dumb.c
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "Dumb.h"
int main(int argc, char **argv)
{
int i, j, flag = 0;
for (i = 1; i < argc; i++)
{
char needle[] = "dumb";
int length = strlen(argv[i]);
for (j = length - 1; j >= 0; j--)
{
printf("%c", argv[i][j]);
argv[i][j] = tolower(argv[i][j]);
}
char *pch = strstr(argv[i], echo);
if(pch)
{
flag = 1;
}
}
if (flag == 1)
{
printf("Dumb was found!\n");
}
return 0;
}
It works perfectly when I try to run it manually from command line using: ./a.out Dumb.
But when I try to use a special test case for it, it just crashes at this line: argv[i][j] = tolower(argv[i][j]);
Here is the code for the testing:
TestLauncher.c
int unit_test(int argc, char **argv);
int main(int argc, char **argv)
{
unit_test(argc, argv);
return 0;
}
Test.c
int __hide_main__(int argc, char **argv);
int unit_test(void)
{
int retval;
char **array;
array = malloc(sizeof(char *) * 2);
array[0] = "./a.out";
array[1] = "Dumb";
retval = __hide_main__(2, array);
free(array);
return retval;
}
When you use the string literal "Dumb", it is read-only, unlike the command-line arguments. To see this, try running the following and you should get the same error:
char *arr = "Dumb";
arr[0] = 'd';
To fix this, you should copy the value of argv into a new array rather than modifying it in-place.
But when I try to use a special test case for it, it just crashes at this line: argv[i][j] = tolower(argv[i][j]);
Your program is having undefined behavior as you are trying to modify the string literal. When a program is having undefined behavior, anything can happen like it may work as expected, it may crash etc.
Change this:
array[1] = "Dumb";
to this:
array[1] = strdup("Dumb");
It works perfectly when I try to run it manually from command line using: ./a.out Dumb.
From C Standards (5.1.2.2.1p2):
The parameters argc and argv and the strings pointed to by the argv array shall be modifiable by the program, and retain their last-stored values between program startup and program termination.
So, it is perfectly fine to modify the argument vector which contains the list of the arguments passed (including program name) when running from command line.
Hence the program is working fine when running from command line.
Also, in your program unit_test function declared as:
int unit_test(int argc, char **argv);
but in the definition the signature is not matching:
int unit_test(void)
The compiler must be giving conflicting types error for this or I believe its a typo while you have edited your program before posting.
Follow the good programming practice, always check the malloc return:
array = malloc(sizeof(char *) * 2);
if (NULL == array) {
fprintf (stderr, "Failed to allocate memory");
exit(EXIT_FAILURE);
}
I need to convert arguments given at command line such as: $ myprogram hello world
and the words need to be printed in CAPS. I am able to to do everything except access the double pointer array to make the changes with toupper()
static char **duplicateArgs(int argc, char **argv)
{
char **copy = malloc(argc * sizeof (*argv));
if(copy == NULL){
perror("malloc returned NULL");
exit(1);
}
int i;
for(i = 0; i<argc; i++){
copy[i] = argv[i];
}
char **temp;
temp = ©[1];
*temp = toupper(copy[1]);
return copy;
}
*temp = toupper(copy[1]);
toupper converts a single character, if you want to convert an entire string:
char *temp = copy[1]; /* You don't need a double pointer */
size_t len = strlen(temp);
for (size_t i = 0; i < len; i++) {
temp[i] = toupper(temp[i]);
}
I assume the argument that is passed into your function char **argv is passed directly from main, so it represents a pointer to the beginning of an array of pointers to each of the command line arguments.
argc represents the number of command line arguments.
Inside your function, you create a new buffer, and then copy the contents of argv into it. So you are creating a copy of the array of pointers to the command line arguments, NOT the command line argument strings themselves.
I am guessing you intended to copy the strings, rather than the pointers to the strings (what would be the point of that?). I suggest you look into the functions strdup and/or strncpy to copy the actual strings.
This also explains with the 'toupper' does not work as you expect - instead of passing a single character to it, you are passing a pointer to a null terminated string of characters.
From the man page of toupper() the function prototype is
int toupper(int c);
In your code, the argument copy[1] is not an int value.
Instead what you want is to check each and every element, if they are in lower case, convert them to upper case. A pseudo-code will look like
for(i = 0; i<argc; i++){
copy[i] = malloc(strlen(argv[i])+ 1); //allocate memory
for (j = 1; j < argc; j++)
for (i = 0; i < strlen(argv[j]); i++)
{
if (islower(argv[j][i])) //check if it is lower case
copy[j-1][i] = toupper(argv[j][i]);
else
copy[j-1][i] = argv[j][i]; //do not convert
}
Consider this example:
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
static char **duplicateArgs(int argc, char **argv)
{
char **copy = NULL;
// allocate memry for pointers to new lines
copy = (char **)malloc(sizeof(char *) * argc);
int line, chr;
for(line = 0; line < argc; line++)
{
// allocate memory for new line
copy[line] = (char *)malloc(sizeof(char) * (strlen(argv[line]) + 1));
// copy with changes
for(chr = 0; chr <= strlen(argv[line]); chr++)
{
copy[line][chr] = toupper(argv[line][chr]);
}
}
return copy;
}
int main(int argc, char * argv[])
{
char ** strs;
int i;
strs = duplicateArgs(argc, argv);
for(i = 0; i < argc; i++)
{
printf("%s\n", strs[i]);
}
return 0;
}
EDIT:
Also you can make a decision about using argv[0] (name of executable file) and change a code if you need. Also checking of malloc result can be added, and other improvements... if you need :-)
You are running into an error using the toupper() function because you are trying to pass in a string instead of an individual letter. Here is an excerpt from the man page describing the function:
DESCRIPTION
The toupper() function converts a lower-case letter to the corresponding
upper-case letter. The argument must be representable as an unsigned
char or the value of EOF.
You have a pointer to a pointer which you could visulize as something like this. In C a string is just an array of chars so you need to dereference twice to get the data in the second level of arrays (the individual letter). Every time you add an * you can think of it as removing one layer of pointers. And you can think of the * operator as the inverse of the & operator.
This line is your problem line
temp = ©[1];
try this instead
//This is a pointer to an individual string
char *temp = copy[1];
//Keep going while there are letters in the string
while(*temp != NULL) {
//convert the letter
toupper(*temp);
//Advance the pointer a letter
temp++;
}
I am trying to get the contents of a directory. Ideally, I would like to store them in a string array. Is there a way to do this in c other than opening the directory, iterating through its contents, and populating an array as it goes?
I am working on a system running OS X 10.9
You can obtain an allocated directory listing with the POSIX scandir function, which takes a path and optional filtering and sorting callbacks, and returns an array of dirent structures. OS X also provides an equivalent function which takes blocks rather than callbacks for sorting and filtering.
int scandir(const char *dirname, struct dirent ***namelist,
int (*select)(const struct dirent *),
int (*compar)(const struct dirent **, const struct dirent **));
Just retrieving an unsorted list of entries is very straightforward:
int num_entries;
struct dirent **entries = NULL;
num_entries = scandir("/", &entries, NULL, NULL);
for(int i = 0; i < num_entries; i++)
puts(entries[i]->d_name);
//entries is ours to free
for(int i = 0; i < num_entries; i++)
free(entries[i]);
free(entries);
POSIX also provides a pre-made sorting function to use with scandir for alphabetical ordering. To use it, just pass alphasort as the last argument.
Be careful of scandir returning an error (-1). The above code is structured in such a way that an explicit check isn't necessary, but that may not be possible in more elaborate uses.
You might want to run using system libc call and fopen.
Here is the sample code, take care of all the array lengths, there is NO validation done here.
#include
#include
#include
int
main(int argc, char* argv[])
{
char cmd[254] = "ls ";
char arr[1024];
char line[254];
FILE *fp;
if(argc < 2) return -1;
if(argv[1]) strcat(cmd, argv[1]);
strcat(cmd, " > /tmp/out");
system(cmd);
fp = fopen("/tmp/out", "r");
if(!fp){
perror("");
return fprintf(stderr, "could not open /tmp/out!\n");
}
while(fgets(line, 254, fp) != NULL) {
strcat(arr, line);
}
printf("%s\n", arr);
return 0;
}
In this program, i want to let user to input 2 arguments, the number
of integer,and the file name.
the file has 10 lines of integer value.
read the file, and put it to inArray[];
and then output it as the end;
notes: For the complete program, i want to make a program that
will scan a file consists of random integer,and then sort
them in ascend order, and print out the first 10 percent
of the sorted integer.
Error: For now, i want to test if it can read the file and put values
into the inArray properly, but its keep getting errors.
warning: initialization makes integer from pointer without a cast
findTotal.c:43:6: warning: passing argument 1 of ‘fopen’
makes pointer from integer without a cast
/usr/include/stdio.h:271:14: note: expected ‘const
char * __restrict__’ but argument is of type ‘char’
Please help me with this, thank you
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
int numOfInt;
char fileName="";
sscanf(argv[1],"%d",&numOfInt);
sscanf(argv[2],"%c",&fileName);
int i, rc;
/* the origninal list , initialise as 0 index*/
int inArray[numOfInt];
/* the number of output int */
int outNumInt = numOfInt * 0.1;
/* the output array of int */
int outArray[outNumInt];
FILE *inFile;
inFile = fopen(fileName,"r");
/* check if the file is empty */
if(inFile==NULL){
printf("can not open the file");
}
for (i = 0; (rc = getc(inFile)) != EOF && i < numOfInt; inArray[i++] = rc)
{
}//for
fclose(inFile);
for(i = 0; i < numOfInt;i++){
printf("%x\n",inArray[i]);
}
}//main
I think you could be using scanf better here. You use it to read in two pieces of information that should have been passed as arguments to the program, and then refreain from using it for what it would actually be good for, which is reading the file in question. Here is my take at this:
#include <stdlib.h>
#include <stdio.h>
int cmp(const void *a, const void *b) { return *(int*)b - *(int*)a; }
int main(int argc, char *argv[])
{
char * ifile = argv[1];
int n = atoi(argv[2]), m = n/10, i;
int nums[n];
FILE * f = fopen(ifile, "r");
for(i = 0; i < n; i++) fscanf(f, "%d", &nums[i]);
qsort(nums, n, sizeof(int), cmp);
for(i = 0; i < m; i++) printf("%d\n",nums[i]);
return 0;
}
If this file is prog.c and the corresponding executable is prog, and your file with numbers is called nums.txt, and contains 100 integers, you would call this as
prog nums.txt 100
The advantage of taking in parameters this way is that it makes repeating the command later easier (all the information needed to repeat it will be in the command history of the shell), and that it is the standard way of passing parameters to a program. It also frees up standard input for other uses.
You have indeed a problem with the filename's management. char is for characters; if you want to handle a file name, you have to use a string. In C, we can use an array of char, terminated by a nul-character. Here, because argv[2] holds directly the name, you can simply use a pointer.
char *fileName = argv[2];
And then:
fopen(fileName, "r");
Since you don't modify the argv pointer, you can also send directly argv[2] as argument.
One of the problems I see in your code is:
char fileName="";
sscanf(argv[2],"%c",&fileName)
A string literal is a constant string, which means you shouldn't attempt to modify it, you should either use a static (or dynamic) char array for that string and use the %s format specifier, or just point fileName to argv[2]
char *fileName;
fileName = argv[2];
I have huge amount of txt files which contains 64x64 matrices consisting of integers. txt files has names like:
mat_1.txt, mat_2.txt, mat_3.txt, mat_4.txt, .... , mat_n.txt.
I have to create a variable, allocate space on host and device, read txt file and copy to device. is it possible to do it all in one loop?
I know how to create a string with sprintf but do not know how to use this string for example for declaring variables.
char fname[10];
for( int k=1; k<=n; k++ )
{
sprintf( fname, "mat_%d", k );
int *fname; // how to say to compiler that insted of `fname` there
// should be `mat_1` and in next cycle `mat_2`?
}
You can't create a variable name at runtime. Variable names are for compiler and only compiler to know and cannot be generated on the fly.
What you need is an array. Since the data already need to be stored in an array, you need to add 1 dimension to your array.
For example, if data in mat_1.txt is a 1 dimensional array, you can have:
int **mat; // 2D array
int k;
mat = malloc(n * sizeof(*mat)); // get an array of pointers (add error checking)
for (k = 1; k <= n; ++k)
{
char fname[20];
FILE *fin;
sprintf(fname, "mat_%d.txt", k);
fin = fopen(fname, "r");
if (fin == NULL)
/* Handle failure */
/* read number of items */
mat[k-1] = malloc(number of items * sizeof(*mat[k-1])); // allocate an array for each file
/* read data of file and put in mat[k-1] */
fclose(fin);
}
/* At this point, data of file mat_i.txt is in array mat[i-1] */
/* When you are done, you need to free the allocated memory: */
for (k = 0; k < n; ++k)
free(mat[k]);
free(mat);
What computer are you using?
A 64x64 array of int, where int is 4 bytes, is array of 16,386 bytes, 22,500 files with 1 matrix/file would be 368,640,000 bytes.
That works fine on my 5 year old laptop:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_FILES (22500)
#define MAX_J (64)
#define MAX_K (64)
int a[MAX_FILES][MAX_J][MAX_K];
const char namefmt[] = "mat_%d";
int main (int argc, const char * argv[]) {
char fname[strlen(namefmt)+10+1]; // an int has 10 or less digits
for (int fileNumber=0; fileNumber<MAX_FILES; ++fileNumber) {
sprintf(fname, namefmt, fileNumber);
FILE* fp = fopen(fname, "r");
if (fp == NULL) {
fprintf(stderr, "Error, can't open file %s\n", fname);
exit(1);
}
for (int j=0; j<MAX_J; ++j) {
for (int k=0; k<MAX_K; ++k) {
//... read the value
}
}
fclose(fp);
}
return 0;
}
It should work okay (though may become painfully slow) on a modern computer running an operating system with virtual memory, and enough swap space. Declaring it as an array, rather than using malloc will save a miniscule amount of space, but otherwise is the same.