Is there a clean way to open a file like this without system calls:
ID*_LogConfig.csv
I tried the following but it didn't worked.
/*Read setup file*/
getcwd(cwd, sizeof(cwd));
snprintf(source, sizeof(source),"%s/ID%*d_LogConfig.csv",cwd);
if( NULL == (input = fopen(source,"r")))
{
snprintf(errbuffer,sizeof(errbuffer), "Could not open file %s - check existence/rights\n", source);
exitHandler(1, errbuffer);
}
It outputs:
/mnt/dataflash/canfilter.d/ID*_LogConfig.csv not found
But with e.g. cat /mnt/dataflash/canfilter.d/ID*_LogConfig.csv it shows the file content.
My compromise solution would be a system call ll ID*_LogConfig.csv and using the output as filename.
This line
snprintf(source, sizeof(source),"%s/ID%*d_LogConfig.csv",cwd);
likely does not produce what you think it does.
The %*d portion is an format specifier with a field-width, per the POSIX printf() documentation
A field width, or precision, or both, may be indicated by an
( '*' ). In this case an argument of type int supplies the
field width or precision. Applications shall ensure that arguments
specifying field width, or precision, or both appear in that order
before the argument, if any, to be converted. A negative field width
is taken as a '-' flag followed by a positive field width. A negative
precision is taken as if the precision were omitted. In format strings
containing the "%n$" form of a conversion specification, a field width
or precision may be indicated by the sequence "*m$", where m is a
decimal integer in the range [1,{NL_ARGMAX}] giving the position in
the argument list (after the format argument) of an integer argument
containing the field width or precision, for example:
printf("%1$d:%2$.*3$d:%4$.*3$d\n", hour, min, precision, sec);
So, this line
snprintf(source, sizeof(source),"%s/ID%*d_LogConfig.csv",cwd);
expects two more integer arguments to be passed. Since you don't pass them, you invoke undefined behavior.
See this answer: https://stackoverflow.com/a/19897395/4756299
Is there a clean way to open a file like this without system calls
No.
fopen() makes use of a system call. You cannot 'open' a file without a system call.
If you're referring to the system(3) function, then you're probably in for some pain - it's best to avoid it if possible, from a performance, reliability and security point of view.
If you want to open 'all files that match the pattern', then look at glob(3), which is likely what your shell is using to handle such wildcards.
You will need to iterate over each of the resulting paths, calling fopen(), fread() and fclose() on each.
Example usage of glob(3):
#include <stdio.h>
#include <glob.h>
void main(void) {
int ret;
int i;
glob_t glob_info;
ret = glob("*.csv", 0, NULL, &glob_info);
if (ret != 0)
{
fprintf(stderr, "glob() failed: %d\n", ret);
return;
}
for (i = 0; i < glob_info.gl_pathc; i++) {
printf("%d: %s\n", i, glob_info.gl_pathv[i]);
}
globfree(&glob_info);
}
It is not really a good idea to open lots of files and treat the stream as a single 'thing' (as you are doing with your cat example).
As #Andrew has pointed out, you must be careful with your use of printf() format strings...
You have provided the following: %s/ID%*d_LogConfig.csv. A % denotes the beginning of a format specifier, you have thus given the following:
%s - a char * (string) parameter follows
%*d - similar to %d, but the * means that the precision is provided as an int parameter, followed by the number itself.
For example:
printf(">%s< >%*d<\n", "Hello", 5, 3);
Will output: (note the 5 characters that the %d outputs)
>Hello< > 3<
If you are after a *, then just put a * in the format string.
If you are after a %, then you need to escape the % but putting %% in the format string.
Ok I solved the "problem" by using the following:
(Processing the output of ls -t and use the newest file as config-file)
/*Search for config-file*/
FILE *file_config;
file_config = popen("ls -t ID*_LogConfig.csv","r");
if (file_config == NULL) {
exitHandler(1, "Error opening date pipe.");
}
fgets(configfile, sizeof(configfile), file_config);
if (strlen(configfile) > 0)
configfile[strlen(configfile)-1] = '\0';
else {
exitHandler(1, "Could not find a ID*_LogConfig.csv\n");
}
getcwd(cwd, sizeof(cwd));
snprintf(source, sizeof(source),"%s/%s",cwd,configfile);
/*Read setup file*/
if( NULL == (input = fopen(source,"r")))
{
snprintf(errbuffer,sizeof(errbuffer), "Could not open file |%s| - check existence/rights\n", source);
exitHandler(1, errbuffer);
}
It seems that this is the only simple way.
Thanks to all.
Related
I am trying to read the data in from a text file, and then divide each value by 3, before printing the output value for each new value.
An example of the format of the text file can be seen below:
0.00707946 -0.0241935 23.9401 0 0.307334 0.2046
As can be seen from the above example, every value is seperated by a space, the number of significant figures vary, and numbers can be positive or negative. I can successfully read through and print out the values to cmd as characters, however, my aim is to divide every single value by the number 3 (an integer) which I am struggling to do.
Is my issue the choice of format specifier I have chosen in my printf statement? Or the choice of explicit casting to float (which I chose to do as some of the numbers are floating point values)
What I have tried so far:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main()
{
char file_name[25];
int current_value = 0;
int new_value;
FILE *fp; //file handle
printf("Enter filename: \n");
gets(file_name);
fp = fopen(file_name, "r"); // read mode
//error handling
if (fp == NULL)
{
perror("Error while opening the file.\n");
getchar();
exit(EXIT_FAILURE);
}
while (fscanf(fp, "%d", ¤t_value) != EOF) //while end of file has not been detected
{
new_value = current_value / 3;
printf("%d ", new_value);
}
fclose(fp);
getchar();
return 0;
}
First, Never, never, Ever use gets(), see Why gets() is so dangerous it should never be used!. With that out of the way, you are attempting to divide each character by 3, not each floating-point value. atoi is an integer conversion for strings, not individual characters.
But all that aside, your at least provided a good-faith attempt at a solution. So let's look at how you can improve things. First, don't use magic-numbers, 25 in your code is a magic-number, instead if you need an integer constant, #define one, e.g.
#define _CRT_SECURE_NO_WARNINGS //preprocessor requirement
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FNMAX 512 /* if you need a constant, define one (or more) */
int main (void) {
Also, Don't skimp on buffer size! On Linux the default PATH_MAX constant is 4096. 25 doesn't even begin to cover allowable filenames.
Next, replace gets with fgets. The only caveat is that you now must trim the trailing '\n' from the buffer. You can do that simply with strcspn (which will report the number of characters that do not include those in the reject string). So choosing a reject string of "\r\n" covers you and strcspn returns the number of character up to the first of either. You simply nul-terminate your string at that index overwriting the line-ending, e.g.
printf ("Enter filename: ");
if (!fgets (file_name, FNMAX, stdin)) { /* validate EVERY input */
fputs ("(user canceled input)\n", stderr);
return 1;
}
file_name[strcspn(file_name, "\r\n")] = 0; /* trim '\n' from end */
Good job on validating your file was open for reading before using fp. Now you simply need to continue in a manner that will read floating-point numbers instead of characters. While I would generally recommend reading the remaining lines into a buffer and then calling sscanf to parse the values from it, you can also just use fscanf to read the floating-point numbers one-after-the-other. (all scanf conversions except "%c" and "%[...]" discard leading whitespace)
You can very simply use fscanf to read, and then divide by 3 (which is where I violate the magic-number rule :), e.g.
/* read/print each floating-point value and value divided by 3 */
while (fscanf (fp, "%lf", &value) == 1)
printf ("\nvalue: %.4f\ndiv-3: %.4f\n", value, value / 3);
That's it. Putting it altogether, you could do:
#define _CRT_SECURE_NO_WARNINGS //preprocessor requirement
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FNMAX 512 /* if you need a constant, define one (or more) */
int main (void) {
char file_name[FNMAX];
double value;
FILE *fp; //file handle
printf ("Enter filename: ");
if (!fgets (file_name, FNMAX, stdin)) { /* validate EVERY input */
fputs ("(user canceled input)\n", stderr);
return 1;
}
file_name[strcspn(file_name, "\r\n")] = 0; /* trim '\n' from end */
/* open/validate file open for reading */
if ((fp = fopen (file_name, "r")) == NULL) {
perror ("fopen-file");
return 1;
}
/* read/print each floating-point value and value divided by 3 */
while (fscanf (fp, "%lf", &value) == 1)
printf ("\nvalue: %.4f\ndiv-3: %.4f\n", value, value / 3);
fclose(fp); /* close file */
getchar(); /* hold terminal open on windows */
return 0;
}
Example Use/Output
$ ./bin/readdivfloats
Enter filename: dat/floats.txt
value: 0.0071
div-3: 0.0024
value: -0.0242
div-3: -0.0081
value: 23.9401
div-3: 7.9800
value: 0.0000
div-3: 0.0000
value: 0.3073
div-3: 0.1024
value: 0.2046
div-3: 0.0682
Compiling From the Command Line
If the reason you have getchar() at the end of your code is to hold the terminal window open after your program finishes due to your using the Visual Studio IDE, you may want to consider just using the command line for small projects. (1) you don't have to set up a project in VS, (2) you can compile many different source files from the same directory in the time it takes to setup another project, and (3) you learn what compiler options you need, so you can then tell the IDE how you want your code compiled.
If using VS, it provides the "VS Developers Command Prompt", which is just a cmd.exe (Command Prompt) with the appropriate path and compiler environment variables set. The VS compiler is cl.exe. All you need to do to compile this code (in filename readdivfloats.c is:
cl /nologo /W3 /wd4996 /Ox /Fereaddivfloats readdivfloats.c
The /Fe option just names the resulting executable, so here it will be readdivfloats.exe in the same directory. I generally like to keep my source directory clean, so I create obj and bin subdirectories to put all the object files and executables in. The /Fo option let's you name the object file (or you can simply name a directory and the object files will be named with the name of the source file). So with that in mind, to put the object file below the .\obj subdirectory and the exe in the .\bin subdirectory, you would use:
cl /nologo /W3 /wd4996 /Ox /Foobj/ /Febin/readdivfloats readdivfloats.c
/W3 turns on full warnings, /wd4996 disables warning 4996, (the annoying #define _CRT_SECURE_NO_WARNINGS warning), Ox turns on fast optimization. You can see all options simply by entering cl /? in the terminal.
I know that there are other questions on this topic but I still can't find the solution to the problem.
I'm trying to read a .csv file in C using the fscanf() function.
If I open the csv file with a text editor it looks like this:
578,2.2212e+05,223,0,243,0,0,0.09,0,0,0,3
633,2.2222e+05,223,0,243,0,0,-0.04,0,0,0,3
688,2.2232e+05,223,0,243,0,0,0.07,0,0,0,3
740,2.2242e+05,223,0,243,0,0,0.04,0,0,0,3
793,2.2252e+05,224,0,244,0,0.01,0.16,0,0,0,3
848,2.2262e+05,223,0,717,0.060985,0.02,0.08,0,0,0,4
902,2.2272e+05,223,0,721,0.084618,0.03,0.24,0,0,0,5
955,2.2282e+05,223,0,730,0.12825,0.05,0.34,0,0,0,4
I just reported the first few rows but it contains a lot more.
Then I used this code to read the file:
FILE* stream = fopen("./filelog.csv", "r");
if(stream == NULL) {
printf("\n file opening failed ");
return -1 ;
}
int values[8];
int count;
for(count = 0; count < 8; count++) {
int u = fscanf(stream, "%i", &values[count]);
printf("%i\n", values[count]);
}
The output I get is the following:
578
32697
0
0
1
0
4199901
0
We can see that only the first value is read correctly. How could I read the whole file and store it into a matrix? I can't find any solution anywhere.
Thank you so much for your replies in advance!
Your format string %i parses a single integer, but the next thing after the integer is a comma, and you're not parsing that. You should always check the return value, to make sure parsing succeeded, before using the value(s).
You need a format string that matches the actual content of the file, something like:
if (fscanf(stream, " %d,%f,%d,%d,%d,%d,%d,%f,%d,%d,%d,%d", ...) == 12)
{
}
in the code above the ... means "put pointers to 12 variables of the proper types, here". The space at the front of the format string is intended to help "consume" whitespace (like linefeeds) in the stream.
For input two files "cl-clean-history" and "cd-clean-history" of similar structure of:
"Lift Convergence"
"Iterations" "cl"
1 1.14094e+00
2 1.14094e+00
and
"Drag Convergence"
"Iterations" "cd"
1 0.14094e+00
2 0.14094e+00
I want to write a program to read the values in second columns in both files and compute its divisor.
My program code is:
#include <stdio.h>
#include <errno.h>
int main (void)
{
double max_cl_cd, cl_cd_temp;
int lines_file;
int tab_line;
double tab_scan_file[200000][4];
int ch=0;
FILE *result_search_cl;
FILE *result_search_cd;
max_cl_cd=0.0;
if((result_search_cl=fopen("cl-clean-history","r"))==NULL)
{
printf("Unable to open input file\n");
}
else
{
lines_file = 0;
FILE *result_search_cd = fopen("cd-clean-history", "r");
FILE *result_search_cl = fopen("cl-clean-history", "r");
while (EOF != (ch=getc(result_search_cd)))
if ('\n' == ch)
++lines_file;
printf("Number of lines in file: %d \n",lines_file);
for (tab_line=0;tab_line<=lines_file;tab_line++) {
fscanf(result_search_cd,"\n");
fscanf(result_search_cd,"\n");
fscanf(result_search_cd,"%d",&tab_scan_file[tab_line][0]);
fscanf(result_search_cd,"\t");
fscanf(result_search_cd,"%f",&tab_scan_file[tab_line][1]);
fscanf(result_search_cd,"\n");
fscanf(result_search_cl,"%d",&tab_scan_file[tab_line][2]);
fscanf(result_search_cl,"\t");
fscanf(result_search_cl,"%f",&tab_scan_file[tab_line][3]);
fscanf(result_search_cl,"\n");
cl_cd_temp=tab_scan_file[tab_line][3]/tab_scan_file[tab_line][1];
if (cl_cd_temp>max_cl_cd)
{
max_cl_cd=cl_cd_temp;
}
printf("%f %f\n",tab_scan_file[tab_line][0],tab_scan_file[tab_line][1]);
}
fclose(result_search_cd);
fclose(result_search_cl);
}
printf("%f %f\n",tab_scan_file[tab_line][0],tab_scan_file[tab_line][1]);
return 0;
}
In my opinion there is something bad in scanf lines, but I don't see what exactly is bad. The first two scans of "\n" are intented to jump two first lines in file. I know that the second option is to perform loop starting from third line (for(tab_line=3; etc.) The %d scans integer value in first column and %f scans float value in second column in both files. Unfortunately, while running in Dev C++ the process exits with return value 3221225725. I found on stackoverflow.com that this value means heap corruption. How to overcome this problem?
I think you should read the fscanf documentation again:
Anyway, fscanf accepts as a second argument a format, so something like "%d" (the one you have used some lines later). When you invoke
fscanf(result_search_cd,"\n");
"\n" is not an identifier. You should replace this line with something like
while (getc(result_search_cd) != "\n"); //Skip first line
while (getc(result_search_cd) != "\n"); //Skip second line
Currently, the logic in glibc source of perror is such:
If stderr is oriented, use it as is, else dup() it and use perror() on dup()'ed fd.
If stderr is wide-oriented, the following logic from stdio-common/fxprintf.c is used:
size_t len = strlen (fmt) + 1;
wchar_t wfmt[len];
for (size_t i = 0; i < len; ++i)
{
assert (isascii (fmt[i]));
wfmt[i] = fmt[i];
}
res = __vfwprintf (fp, wfmt, ap);
The format string is converted to wide-character form by the following code, which I do not understand:
wfmt[i] = fmt[i];
Also, it uses isascii assert:
assert (isascii(fmt[i]));
But the format string is not always ascii in wide-character programs, because we may use UTF-8 format string, which can contain non-7bit value(s).
Why there is no assert warning when we run the following code (assuming UTF-8 locale and UTF-8 compiler encoding)?
#include <stdio.h>
#include <errno.h>
#include <wchar.h>
#include <locale.h>
int main(void)
{
setlocale(LC_CTYPE, "en_US.UTF-8");
fwide(stderr, 1);
errno = EINVAL;
perror("привет мир"); /* note, that the string is multibyte */
return 0;
}
$ ./a.out
привет мир: Invalid argument
Can we use dup() on wide-oriented stderr to make it not wide-oriented? In such case the code could be rewritten without using this mysterious conversion, taking into account the fact that perror() takes only multibyte strings (const char *s) and locale messages are all multibyte anyway.
Turns out we can. The following code demonstrates this:
#include <stdio.h>
#include <wchar.h>
#include <unistd.h>
int main(void)
{
fwide(stdout,1);
FILE *fp;
int fd = -1;
if ((fd = fileno (stdout)) == -1) return 1;
if ((fd = dup (fd)) == -1) return 1;
if ((fp = fdopen (fd, "w+")) == NULL) return 1;
wprintf(L"stdout: %d, dup: %d\n", fwide(stdout, 0), fwide(fp, 0));
return 0;
}
$ ./a.out
stdout: 1, dup: 0
BTW, is it worth posting an issue about this improvement to glibc developers?
NOTE
Using dup() is limited with respect to buffering. I wonder if it is considered in the implementation of perror() in glibc. The following example demonstrates this issue.
The output is done not in the order of writing to the stream, but in the order in which the data in the buffer is written-off.
Note, that the order of values in the output is not the same as in the program, because the output of fprintf is written-off first (because of "\n"), and the output of fwprintf is written off when program exits.
#include <wchar.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
wint_t wc = L'b';
fwprintf(stdout, L"%lc", wc);
/* --- */
FILE *fp;
int fd = -1;
if ((fd = fileno (stdout)) == -1) return 1;
if ((fd = dup (fd)) == -1) return 1;
if ((fp = fdopen (fd, "w+")) == NULL) return 1;
char c = 'h';
fprintf(fp, "%c\n", c);
return 0;
}
$ ./a.out
h
b
But if we use \n in fwprintf, the output is the same as in the program:
$ ./a.out
b
h
perror() manages to get away with that, because in GNU libc stderr is unbuffered. But will it work safely in programs where stderr is manually set to buffered mode?
This is the patch that I would propose to glibc developers:
diff -urN glibc-2.24.orig/stdio-common/perror.c glibc-2.24/stdio-common/perror.c
--- glibc-2.24.orig/stdio-common/perror.c 2016-08-02 09:01:36.000000000 +0700
+++ glibc-2.24/stdio-common/perror.c 2016-10-10 16:46:03.814756394 +0700
## -36,7 +36,7 ##
errstring = __strerror_r (errnum, buf, sizeof buf);
- (void) __fxprintf (fp, "%s%s%s\n", s, colon, errstring);
+ (void) _IO_fprintf (fp, "%s%s%s\n", s, colon, errstring);
}
## -55,7 +55,7 ##
of the stream. What is supposed to happen when the stream isn't
oriented yet? In this case we'll create a new stream which is
using the same underlying file descriptor. */
- if (__builtin_expect (_IO_fwide (stderr, 0) != 0, 1)
+ if (__builtin_expect (_IO_fwide (stderr, 0) < 0, 1)
|| (fd = __fileno (stderr)) == -1
|| (fd = __dup (fd)) == -1
|| (fp = fdopen (fd, "w+")) == NULL)
NOTE: It wasn't easy to find concrete questions in this post; on the whole, the post seems to be an attempt to engage in a discussion about implementation details of glibc, which it seems to me would be better directed to a forum specifically oriented to development of that library such as the libc-alpha mailing list. (Or see https://www.gnu.org/software/libc/development.html for other options.) This sort of discussion is not really a good match for StackOverflow, IMHO. Nonetheless, I tried to answer the questions I could find.
How does wfmt[i] = fmt[i]; convert from multibyte to wide character?
Actually, the code is:
assert(isascii(fmt[i]));
wfmt[i] = fmt[i];
which is based on the fact that the numeric value of an ascii character is the same as a wchar_t. Strictly speaking, this need not be the case. The C standard specifies:
Each member of the basic character set shall have a code value equal to its value when used as the lone character in an integer character constant if an implementation does not define __STDC_MB_MIGHT_NEQ_WC__. (§7.19/2)
(gcc does not define that symbol.)
However, that only applies to characters in the basic set, not to all characters recognized by isascii. The basic character set contains the 91 printable ascii characters as well as space, newline, horizontal tab, vertical tab and form feed. So it is theoretically possible that one of the remaining control characters will not be correctly converted. However, the actual format string used in the call to __fxprintf only contains characters from the basic character set, so in practice this pedantic detail is not important.
Why there is no assert warning when we execute perror("привет мир");?
Because only the format string is being converted, and the format string (which is "%s%s%s\n") contains only ascii characters. Since the format string contains %s (and not %ls), the argument is expected to be char* (and not wchar_t*) in both the narrow- and wide-character orientations.
Can we use dup() on wide-oriented stderr to make it not wide-oriented?
That would not be a good idea. First, if the stream has an orientation, it might also have a non-empty internal buffer. Since that buffer is part of the stdio library and not of the underlying Posix fd, it will not be shared with the duplicate fd. So the message printed by perror might be interpolated in the middle of some existing output. In addition, it is possible that the multibyte encoding has shift states, and that the output stream is not currently in the initial shift state. In that case, outputting an ascii sequence could result in garbled output.
In the actual implementation, the dup is only performed on streams without orientation; these streams have never had any output directed at them, so they are definitely still in the initial shift state with an empty buffer (if the stream is buffered).
Is it worth posting an issue about this improvement to glibc developers?
That is up to you, but don't do it here. The normal way of doing that would be to file a bug. There is no reason to believe that glibc developers read SO questions, and even if they do, someone would have to copy the issue to a bug, and also copy any proposed patch.
it uses isascii assert.
This is OK. You are not supposed to call this function. It is a glibc internal. Note the two underscores in front of the name. When called from perror, the argument in question is "%s%s%s\n", which is entirely ASCII.
But the format string is not always ascii in wide-character programs, because we may use UTF-8
First, UTF-8 has nothing to do with wide characters. Second, the format string is always ASCII because the function is only called by other glibc functions that know what they are doing.
perror("привет мир");
This is not the format string, this is one of the arguments that corresponds to one of the %s in the actual format string.
Can we use dup() on wide-oriented stderr
You cannot use dup on a FILE*, it operates on POSIX
file descriptors that don't have orientation.
This is the patch that I would propose to glibc developers:
Why? What isn't working?
My application is basically a shell which expects an input of type cmd [x], where cmd is constant and x is optional. So cmd 1 is legal as well as cmd by itself - then I assume a default parameter for x.
I am doing this:
char cmd[64];
scanf("%s", cmd);
int arg;
scanf("%d", &arg); // but this should be optional
How can I read the integer parameter, and set it to a default if none is currently available in the prompt? I do not want the prompt to wait for additional input if it was not given in the original command.
I tried several versions using fgetc() and getchar() and comparing them to EOF but to no avail. Each version I tried ends up waiting on that optional integer parameter.
The easy way:
char b[73]; //powers of 2 magic is evil.
if(fgets(b,sizeof b,stdin) != NULL) {
char cmd[59]; //59, no command will ever be > 58. Ever I say.
int arg;
if(sscanf(b,"%58s %d",cmd,&arg) == 2) {
//handle cmd and arg
} else if(sscanf(b,"%58s",cmd) == 1) {
//handle cmd only
} else {
// :-/
}
}
Simple answer, you can't. The C runtime takes input from the OS, but doesn't control it. To do something like this you will need to interact directly with the OS using platform specific APIs.
Are you reading line-by-line? Can't you just read the whole command until you reach a "\n" (newline)? If you get two tokens before the newline, it is a command and the argument; if you read only one, it is the command only and you set the second argument to the default.
Here's a program that works (sorry my previous answer was made in haste).
int main(){
char cmd[100], line[100];
int man = 0;
printf("OK: ");
fgets(line, 100, stdin);
int num = sscanf(line,"%s %d",cmd,&man);
if (num==1)printf("one! %s %d\n", cmd, man);
else if (num==2)printf("two! %s %d\n", cmd, man);
}
fgets reads the line (with bounds checking), and sscanf will assess whether one or two tokens were entered.