C: Converting strings of doubles in a module (AKA in the kernel) - c

I'd love to use the outdated "atoi" kind of stuff, but it turns out I don't have it in kernel space.
I have this code in the write method of my module. Writes are being controlled by me in user space, and it so happens that the user will always write something in this format:
"0.9 9.5 7.6 "
Hence - I have this code to parse it:
ssize_t write_info( struct file *filp, const char __user *buff, unsigned long len, void *data )
{
char *seedid;
char *low_in;
char *high_in;
char *dropper;
unsigned long long seedid_var;
double d_1;
double d_2;
printk(KERN_INFO "Whaddup u writin'?\n");
dropper = kstrdup(buff, 0);
seedid = strsep(&dropper, " ");
printk("HERE IS: %s\n\n", seedid);
sscanf(seedid, "%lld", &seedid_var);
printk("AND BACK AGAIN: %lld\n\n\n", seedid_var);
low_in = strsep(&dropper, " ");
printk("HERE IS: %s\n\n", low_in);
sscanf(low_in, "%f", &d_1);
printk("AND BACK AGAIN: %f\n\n", d_1);
high_in = strsep(&dropper, " ");
printk("HERE IS: %s\n\n", high_in);
sscanf(high_in, "%f", &d_2);
printk("AND BACK AGAIN: %f\n\n", d_2);
...
I then trigger my module by echo'ing on the procfile it creates (calling my write method) like this:
echo "0.9 9.8 3.4 " > name_of_my_proc
With dmesg:
[ 2211.808474] Whaddup u writin'?
[ 2211.808505] HERE IS: 0.9
[ 2211.808508]
[ 2211.808514] AND BACK AGAIN: 0
[ 2211.808516]
[ 2211.808517]
[ 2211.808520] HERE IS: 9.8
[ 2211.808522]
[ 2211.808524] AND BACK AGAIN: %f
[ 2211.808526]
[ 2211.808529] HERE IS: 3.4
[ 2211.808531]
[ 2211.808533] AND BACK AGAIN: %f
When printing back to the kernel...nothing performs like I'd like! My 0.9 gets killed off to 0 (is that not a legit long long value, if so that would make sense). Most importantly, my doubles don't get converted - it just prints the character %f. What can I do to make them print as I typed them?
Thanks

You are using wrong scan codes.
Scan code: Data type:
%d int
%ld long
%f float
%lf double
Also note that long long is an integer data type and cannot store fractional numbers.

Related

How can fscanf(), in C, be used to read a .gro file?

I am trying to read the following gro file via a C code.
FJP in Pol Water in water t= 0.00000 step= 0
16
1FJP P 1 5.346 7.418 0.319
2FJP P 2 5.151 7.405 0.499
3FJP P 3 5.260 7.178 0.428
4FJP P 4 5.159 6.961 0.342
5FJP P 5 5.355 6.909 0.220
6FJP P 6 5.169 6.824 0.043
7FJP P 7 5.068 6.669 11.454
8FJP P 8 4.919 6.861 11.482
9FJP P 9 4.835 7.075 11.364
10FJP P 10 4.738 6.987 11.197
11FJP P 11 4.847 7.115 10.993
12FJP P 12 4.642 7.126 10.870
13FJP P 13 4.680 6.940 10.674
14FJP P 14 4.521 7.052 10.545
15FJP P 15 4.321 6.973 10.513
16FJP P 16 4.315 6.728 10.516
11.56681 11.56681 11.56681
My code is:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char * argv[])
{
char input_file[]="file.gro";
FILE *input;
char *myfile=malloc(sizeof(char)*80);
sprintf(myfile,"%s",input_file); //the .gro file being read in
input=fopen(myfile,"r");
double dummy1,dummy6,dummy7,dummy8,dummy9,dummy10,dummy11;
int dummy2,dummy3,dummy4,dummy5;
int lines=0;
while (fscanf(input,"FJP in Pol Water in water t= %lf step= %d",&dummy1,&dummy2)==2
||fscanf(input," %d\n",&dummy3)==1
||fscanf(input," %dFJP P %d %lf %lf %lf\n",
&dummy4,&dummy5,&dummy6,&dummy7,&dummy8)==5
||fscanf(input," %lf %lf %lf\n",&dummy9,&dummy10,&dummy11)==3)
{
printf("%lf %d\n",dummy1,dummy2);
printf("%d\n",dummy3);
printf("%d %d\n",dummy4,dummy5);
printf("%lf %lf %lf\n",dummy6,dummy7,dummy8);
printf("%lf %lf %lf\n",dummy9,dummy10,dummy11);
lines=lines+1;
}
printf("lines=%d\n",lines);
fclose(input);
}
The problem is the values printed by the various dummy variables do not match what is in the file. Also, the number of lines being read is 3 as opposed to 19, which matches the file. I am not certain what is incorrect about my fscanf() statements to read this file. Any help for this problem would be much appreciated.
Your main problem is that you are assuming scanf is better than it is.
Scanf will read and parse as many arguments as it can, and then give up. It does not rewind to the start of the scanf. Also it treats spaces and newlines (and tabs) as simply "skip all whitespace"
So the line printf("%d\n",dummy3) will try to parse the main lines, eg 1FJP
It will read the digit 1 OK into dummy3, but then get stuck because P != a whitespace.
All the other rules will then get stuck, because none of them expect a P or any string first.
If you want to do it this way, you will just have to apply the scanf statements more intelligently as and when they are expected.
The problem is that you try to read and match the header repeatedly, before each line read (in the while loop.) you should read the head once, then read the lines. You also only need to skip any given piece of whitespace once. So you end up with code like:
if (fscanf(input,"FJP in Pol Water in water t=%lf step=%d%d", &dummy1, &dummy2, &dummy3) != 3) {
fprintf(stderr, "Invalid header\n");
exit(1); }
while (fscanf(input,"%dFJP P%d%lf%lf%lf", &dummy4, &dummy5, &dummy6, &dummy7, &dummy8) == 5) {
... read a line of the table

Q: About reading data files using fscanf()

Am using Turbo C in a DOS emulator (Dosbox). In the following lines, I am trying to read integer and float data but only get the first (int) field. Have found much Q & A on the subject of reading files using fscanf() and, specifically, with space-delimited data but relevant info was scant or missing (mostly from the questions). Here is code demonstrating the problem:
#include <stdio.h>
int index;
float rtime, volts;
char infilename[10];
int *pti;
float *ptx;
float *pty;
FILE *infp;
void main(void)
{
infp = fopen("data1", "r");
pti = &index;
ptx = &rtime;
pty = &volts;
fscanf(infp, "%d %6.3f %6.3f", &index, &rtime, &volts);
printf("%3d %6.3f %6.3f\n", index, rtime, volts);
}
Here is the first line from the data file:
37 261.100 0.996
printf gives the following output:
37 0.000 0.000
Any obvious goofs? thx
The format %6.3f is incorrect for scanf(). You probably want %f, or possibly %7f. You cannot specify the number of decimals in a scanf() format.

fwrite cuts off character string

I am trying to output the statistics from my program into a file. I first create some empty strings, then amend them using sprintf, this is because I need to turn floats into chars. I then write them to a file.
I sort of works, but the output .txt file only returns 4 digits of precision regardless what I specify in sprintf.
CODE:
METRIC.RESP_TIME =(( (long int )(tval_after.tv_sec*1000000 + tval_after.tv_usec) - (long int )(tval_before.tv_sec*1000000 + tval_before.tv_usec)));
METRIC.RESP_TIME = (float) METRIC.RESP_TIME/1000000;
float ave_resp_time = METRIC.RESP_TIME/R;
float ave_through = METRIC.BYTES_RECEIVED/METRIC.RESP_TIME;
FILE *fp;
char size_str [30]; //malloc(((int)strlen(DOWNLOAD_FILE)+ (int)strlen(Q[LOCAL_QUEUE_COUNT].CHORE_NAME)))];
char rate_str [30];
char through_put_str [30];
sprintf(size_str,"TOTAL BYTES RECIEVED [B]: %5.0d ", METRIC.BYTES_RECEIVED);
sprintf(rate_str,"TOTAL TIME REQUIRED [s]: %2.8f ", ave_resp_time);
sprintf(through_put_str,"AVERAGE THROUGHPUT [B/s]: %2.8f ", ave_through);
fprintf(stdout,"%d\n",METRIC.BYTES_RECEIVED);
fp = fopen( METRICS_FILE, "w");
if(NULL == fp){
printf("Could not make metrics file: error %d ",errno);
return 0;
}
fwrite(size_str,(size_t)sizeof(size_str),1,fp);
fwrite(rate_str,sizeof(rate_str),1,fp);
fwrite(through_put_str,sizeof(through_put_str),1,fp);
fclose(fp);
return 0;
OUTPUT:
TOTAL BYTES RECIEVED [B]: 5526TOTAL TIME REQUIRED [s]: 0.001AVERAGE THROUGHPUT [B/s]: 2992
Hoping to make it look like:
TOTAL BYTES RECIEVED [B]: 55264892
TOTAL TIME REQUIRED [s]: 0.0019634
AVERAGE THROUGHPUT [B/s]: 29929054
You need an explicit new line character; fwrite() does not add one, and also you can directly use fprintf() instead of what you have.
To fix your code, do this
sprintf(size_str, "TOTAL BYTES RECIEVED [B]: %5.0d\n", METRIC.BYTES_RECEIVED);
/* ^ this will break the line */
the strcat(size_str, "\0"); is not needed.
You don't have to do all this, because you can just
fprintf(fp, "TOTAL BYTES RECIEVED [B]: %5.0d\n", METRIC.BYTES_RECEIVED);
/* ^ this will break the line */
As mentioned by #Jonathan Leffler, use well sized buffers rather than hoping that 30 is ample.
One method that has worked well is to size the buffer per the sprintf()
// char size_str [30];
// sprintf(size_str,"TOTAL BYTES RECIEVED [B]: %5.0d ", METRIC.BYTES_RECEIVED);
#define INT_MAX_PRT (sizeof(int) * CHAR_BIT/3 + 3)
const char rcv_fmt[] = "TOTAL BYTES RECEIVED [B]: %5.0d\n";
char size_str [sizeof rcv_fmt + INT_MAX_PRT];
sprintf(size_str, rcv_fmt, METRIC.BYTES_RECEIVED);
This approach is a bit more challenging with floating point as the "%f" width could be so large.
// char rate_str [30];
// sprintf(rate_str,"TOTAL TIME REQUIRED [s]: %2.8f ", ave_resp_time);
#define FLT_MAX_PRT (1 /* sign */ + FLT_MAX_10_EXP + 1)
const char *time_fmt[] = "TOTAL TIME REQUIRED [s]: %2.8f\n";
char rate_str[sizeof time_fmt + FLT_MAX_PRT + 8];
sprintf(rate_str, time_fmt, ave_resp_time);
Still, since it is possible to mistake the needed buffer size, code could also use snprintf() to minimize the harm. But in the end, a proper size buffer is needed.
Note: added '\n' to the formats.
First things first, you should get out of the habit of using sizeof on any array. This will get you into trouble faster than you realize.
Second, you seem to be doing double-duty here. There is no need to use sprintf + fwrite at all. You should just fprintf and pass you fp as the first arg.

c - fscanf segmentation fault

Really strange problem with fscanf. It seems as if it can't find the file. Heres the code:
char obs_file[255];
FILE *obs_fp;
strcpy(obs_file, "/aber/dap/cetaceans/data/observers_1.txt");
obs_fp = fopen(obs_file, "r");
date_time t;
fscanf(obs_fp, "%d %d %d %d %d %d\n", &t.day, &t.mth, &t.yr, &t.hrs, &t.mns, &t.scs); //This line runs fine
obs_head.obs->time = t;
printf("%d %d %d %d %d %d\n", t.day, t.mth, t.yr, t.hrs, t.mns, t.scs);
while(feof(obs_fp) == 0) {
char id[5];
char a[7];
char b[7];
location loc;
double lng = 0.0, lat = 0.0;
fscanf(obs_fp, "%s %lf %lf", id, &lat, &lng); //Seg fault here on first run of loop
loc.lat = lat;
loc.lng = lng;
add_obs_node(make_obs_node(id, loc, t));
}
File to be read:
05 11 2014 14 53 00
AB01 52.408 -4.217
It seems like the file pointer has changed somewhere around the while statement, I would understand if I was reading over the end of file, but it fails while there are definitely lines left. Also, I know Im opening the file right, as the first fscanf runs fine.
Any ideas?
Wrong use of feof() and unlimited fscanf("%s"...
feof() reports if EOF occurred due to previous IO, not if it is about to occur.
Use instead
char id[5];
double lng = 0.0, lat = 0.0;
while(fscanf(obs_fp, "%4s%lf%lf", id, &lat, &lng) == 3) {
loc.lat = lat;
loc.lng = lng;
add_obs_node(make_obs_node(id, loc, t));
}
I suspect original code failed on the 2nd iteration. Assume the last data in the file was "AB01 52.408 -4.217\n". fscanf(obs_fp, "%s %lf %lf" would scan up to the "\n" and put "\n" back into stdin as it is not part of a double. EOF flag is not set. The use of feof() signals no EOF. So fscanf(obs_fp, "%s %lf %lf" happens again, but no data is save in id, as "%s" consume leading white-space but has not non-white-space to save. Code does not check the fscanf() return value (bad), but assumes good data in id, which may be junk. Then add_obs_node() is called with an invalid string id.
Other failure mechanisms could have occurred too - need to see more code.
Bottom line: Check fscanf() results. Limit string input.
Minor: Note that the spaces between "%d %d" are not needed, but OK to have. The final "\n" is also OK but not needed. It is not simply consuming the following '\n', but any and all following white-space.
if (6 != fscanf(obs_fp, "%d%d%d%d%d%d",
&t.day, &t.mth, &t.yr, &t.hrs, &t.mns, &t.scs)) {
Handle_BadData();
}

How can I format currency with commas in C?

I'm looking to format a Long Float as currency in C. I would like to place a dollar sign at the beginning, commas iterating every third digit before decimal, and a dot immediately before decimal. So far, I have been printing numbers like so:
printf("You are owed $%.2Lf!\n", money);
which returns something like
You are owed $123456789.00!
Numbers should look like this
$123,456,789.00
$1,234.56
$123.45
Any answers need not be in actual code. You don't have to spoon feed. If there are C-related specifics which would be of help, please mention. Else pseudo-code is fine.
Your printf might already be able to do that by itself with the ' flag. You probably need to set your locale, though. Here's an example from my machine:
#include <stdio.h>
#include <locale.h>
int main(void)
{
setlocale(LC_NUMERIC, "");
printf("$%'.2Lf\n", 123456789.00L);
printf("$%'.2Lf\n", 1234.56L);
printf("$%'.2Lf\n", 123.45L);
return 0;
}
And running it:
> make example
clang -Wall -Wextra -Werror example.c -o example
> ./example
$123,456,789.00
$1,234.56
$123.45
This program works the way you want it to both on my Mac (10.6.8) and on a Linux machine (Ubuntu 10.10) I just tried.
I know this is a way-old post, but I disappeared down the man rabbit hole today, so I thought I'd document my travels:
There's a function called strfmon() that you can include with monetary.h that will do this, and do it according to local or international standards.
Note that it works like printf(), and will take as many double arguments as there are % formats specified in the string.
There's a lot more to it than what I have here, and I found this page to be the most helpful: https://www.gnu.org/software/libc/manual/html_node/Formatting-Numbers.html
#include <monetary.h>
#include <locale.h>
#include <stdlib.h>
#include <stdio.h>
int main(){
// need to setlocal(), "" sets locale to the system locale
setlocale(LC_ALL, "");
double money_amt = 1234.5678;
int buf_len = 16;
char simple_local[buf_len];
char international[buf_len];
char parenthesis_for_neg[buf_len];
char specified_width[buf_len];
char fill_6_stars[buf_len];
char fill_9_stars[buf_len];
char suppress_thousands[buf_len];
strfmon(simple_local, buf_len-1, "%n", money_amt);
strfmon(international, buf_len-1, "%i", money_amt);
strfmon(parenthesis_for_neg, buf_len-1, "%(n", money_amt);
strfmon(specified_width, buf_len-1, "%#6n", money_amt);
strfmon(fill_6_stars, buf_len-1, "%=*#6n", money_amt);
strfmon(fill_9_stars, buf_len-1, "%=*#8n", money_amt);
strfmon(suppress_thousands, buf_len-1, "%^=*#8n", money_amt);
printf( "===================== Output ===================\n"\
"Simple, local: %s\n"\
"International: %s\n"\
"parenthesis for negatives: %s\n"\
"fixed width (6 digits): %s\n"\
"fill character '*': %s\n"\
"-- note fill characters don't\n"\
"-- count where the thousdands\n"\
"-- separator would go:\n"\
"filling with 9 characters: %s\n"\
"Suppress thousands separators: %s\n"\
"================================================\n",
simple_local, international, parenthesis_for_neg,
specified_width, fill_6_stars, fill_9_stars,
suppress_thousands);
/** free(money_string); */
return 0;
}
===================== Output ===================
Simple, local: $1,234.57
International: USD1,234.57
parenthesis for negatives: $1,234.57
fixed width (6 digits): $ 1,234.57
fill character '*': $**1,234.57
-- note fill characters don't
-- count where the thousdands
-- separator would go:
filling with 9 characters: $*****1,234.57
Suppress thousands separators: $****1234.57
================================================
I don't think there's a C function to do that, but you could just write your own? Say float price = 23234.45. First print (int)price with commas, print a decimal point; then for the decimal part, do printf("%d", (int)(price*100)%100);
int anio, base = 1e4;
double cantidad, rata = 0.5;
int din_buf = 16;
char dinero[din_buf];
printf("%3s%23s\n", "Año", "Cantidad a depositar");
setlocale(LC_ALL, "en_US");
for ( anio = 1; anio < 11; anio++) {
cantidad = base * pow(rata + 1, anio);
strfmon(dinero, din_buf, "%#6n", cantidad);
printf("%3d\t%s\n", anio, dinero);
}
Windows users (with MSVC)
You cannot use:
the POSIX printf() formatting extras in Cal Norum’s answer.
the GNU strfmon() function in rreagan3’s and Edgar Fernando Dagar’s answers.
You are kind of stuck using the Win32 API:
GetCurrencyFormatEx() for locale-dependent currency formatting
(GetNumberFormatEx() for locale-dependent general-purpose number formatting)
Example:
#include <stdio.h>
#include <wchar.h>
#include <windows.h>
int main(void)
{
double money = 1234567.89;
wchar_t s[20], money_s[20];
swprintf( s, sizeof s, L"%.2f", money );
GetCurrencyFormatEx( L"en_US", 0, s, NULL, money_s, (int)sizeof money_s );
printf( "You are owed %S!\n", money_s );
}
You are owed $1,234,567.89!
As always, watch your rounding errors with swprintf() if you are counting currency with more precision than 100ths. (You may not want to round up if you owe money.)

Resources