C convert section of char array to double - c

I want to convert a section of a char array to a double. For example I have:
char in_string[] = "4014.84954";
Say I want to convert the first 40 to a double with value 40.0. My code so far:
#include <stdio.h>
#include <stdlib.h>
int main(int arg) {
char in_string[] = "4014.84954";
int i = 0;
for(i = 0; i <= sizeof(in_string); i++) {
printf("%c\n", in_string[i]);
printf("%f\n", atof(&in_string[i]));
}
}
In each loop atof it converts the char array from the starting pointer I supply all the way to the end of the array. The output is:
4
4014.849540
0
14.849540
1
14.849540
4
4.849540
.
0.849540
8
84954.000000 etc...
How can I convert just a portion of a char array to a double? This must by modular because my real input_string is much more complicated, but I will ensure that the char is a number 0-9.

The following should work assuming:
I will ensure that the char is a number 0-9.
double toDouble(const char* s, int start, int stop) {
unsigned long long int m = 1;
double ret = 0;
for (int i = stop; i >= start; i--) {
ret += (s[i] - '0') * m;
m *= 10;
}
return ret;
}
For example for the string 23487 the function will do this calculations:
ret = 0
ret += 7 * 1
ret += 8 * 10
ret += 4 * 100
ret += 3 * 1000
ret += 2 * 10000
ret = 23487

You can copy the desired amount of the string you want to another char array, null terminate it, and then convert it to a double. EG, if you want 2 digits, copy the 2 digits you want into a char array of length 3, ensuring the 3rd character is the null terminator.
Or if you don't want to make another char array, you can back up the (n+1)th char of the char array, replace it with a null terminator (ie 0x00), call atof, and then replace the null terminator with the backed up value. This will make atof stop parsing where you placed your null terminator.

Just use sscanf. Use the format "ld" and check for return value is one.

What about that, insert NULL at the right position and then revert it back to the original letter? This means you will manipulate the char array but you will revert it back to the original at the end.

You can create a function that will make the work in a temporary string (on the stack) and return the resulting double:
double atofn (char *src, int n) {
char tmp[50]; // big enough to fit any double
strncpy (tmp, src, n);
tmp[n] = 0;
return atof(tmp);
}

How much simpler could it get than sscanf?
#include <assert.h>
#include <stdio.h>
int main(void) {
double foo;
assert(sscanf("4014.84954", "%02lf", &foo) == 1);
printf("Processed the first two bytes of input and got: %lf\n", foo);
assert(sscanf("4014.84954" + 2, "%05lf", &foo) == 1);
printf("Processed the next five bytes of input and got: %lf\n", foo);
assert(sscanf("4014.84954" + 7, "%lf", &foo) == 1);
printf("Processed the rest of the input and got: %lf\n", foo);
return 0;
}

Related

Parse string to number in C

I tried to write a function to convert a string to an int:
int convert(char *str, int *n){
int i;
if (str == NULL) return 0;
for (i = 0; i < strlen(str); i++)
if ((isdigit(*(str+i))) == 0) return 0;
*n = *str;
return 1;
}
So what's wrong with my code?
*n = *str means:
Set the 4 bytes of memory that n points to, to the 1 byte of memory that str points to. This is perfectly fine but it's probably not your intention.
Why are you trying to convert a char* to an int* in the first place? If you literally just need to do a conversion and make the compiler happy, you can just do int *foo = (int*)bar where bar is the char*.
Sorry, I don't have the reputation to make this a comment.
The function definitely does not perform as intended.
Here are some issues:
you should include <ctype.h> for isdigit() to be properly defined.
isdigit(*(str+i)) has undefined behavior if str contains negative char values. You should cast the argument:
isdigit((unsigned char)str[i])
the function returns 0 if there is any non digit character in the string. What about "-1" and "+2"? atoi and strtol are more lenient with non digit characters, they skip initial white space, process an optional sign and subsequent digits, stopping at the first non digit.
the test for (i = 0; i < strlen(str); i++) is very inefficient: strlen may be invoked for each character in the string, with O(N2) time complexity. Use this instead:
for (i = 0; str[i] != '\0'; i++)
*n = *str does not convert the number represented by the digits in str, it merely stores the value of the first character into n, for example '0' will convert to 48 on ASCII systems. You should instead process every digit in the string, multiplying the value converted so far by 10 and adding the value represented by the digit with str[i] - '0'.
Here is a corrected version with your restrictive semantics:
int convert(const char *str, int *n) {
int value = 0;
if (str == NULL)
return 0;
while (*str) {
if (isdigit((unsigned char)*str)) {
value = value * 10 + *str++ - '0';
} else {
return 0;
}
}
*n = value;
return 1;
}
conversion of char* pointer to int*
#include
main()
{
char c ,*cc;
int i, *ii;
float f,*ff;
c = 'A'; /* ascii value of A gets
stored in c */
i=25;
f=3.14;
cc =&c;
ii=&i;
ff=&f;
printf("\n Address contained
in cc =%u",cc);
printf("\n Address contained
in ii =%u",ii);
printf(:\n Address contained
in ff=%u",ff);
printf(\n value of c= %c",
*cc);
printf(\n value of i=%d",
**ii);
printf(\n value of f=%f",
**ff);
}

Manipulating Big Numbers as strings

I have a problem that goes:
Create a C program that inputs large integers as strings.
Then every character is converted into the corresponding digit.
After that I have to create a function addBigNumbers() that has 3 matrices.
addBigNumbers(char *a1, char *a2, char *res)
a1 and a2 will contain the 2 large numbers that I want to add,res will contain the sum of those as a number sequence. We want the function that we created to check if the strings contains numbers only.
If it contains only numbers then res equals 1 and it prints the sum of those numbers else res is equal to 0 (max number length is 1000)
After that first function we want to create a function for subtraction.
So far I haven't gotten to subtraction since I stuck in the first one and I need your help.
This is the code that I have so far:
#include <stdio.h>
#include <stdlib.h>
#define N 1000
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int addHugeNumbers(char *a1, char *a2, char *res){
int y=0, u=0, h=0;
res=strcat(a1,a2);
if(strlen(a1)>strlen(a2)){
y=atoi(a1);
u=atoi(a2);
h=y+u;
}
else{
y=atoi(a1);
u=atoi(a2);
h=u+y;
}
printf("%d", h);
}
int main(int argc, char *argv[]) {
char res[N];
char a1[N/2];
char a2[N/2];
scanf("%s", &a1);
scanf("%s", &a2);
addHugeNumbers(a1, a2, res);
return 0;
}
The problem I have is that if I input ex. 23 23 it outputs 2346 which is obviously wrong but it got 46 correct, when I input 1234 123 it outputs 1234246 which is all wrong.
Where it gets weird is if i input something like 1234r 123 or anything else that has a character in it, it outputs the exact sum.
The problem is res=strcat(a1,a2), which does something very different than that what you think: it appends a2 to a1, and it does not "create" a new string. See, for example strcat-definition at cppreference.com:
char *strcat( char *dest, const char *src )
Appends a copy of the null-terminated byte string pointed to by src
to the end of the null-terminated byte string pointed to by dest. The
character src[0] replaces the null terminator at the end of dest. The
resulting byte string is null-terminated.
So you are manipulating your input before calculating something, and that's what you will observe then when using a debugger.
Further, scanf("%s", &a1) looks suspicious; it should be scanf("%s", a1);. Your compiler should have warned you.
You'd probably rethink addBigNumbers, probably adding the digits in a loop rather than converting them to (somehow always) limited integral data types in between. This task is actually nothing for beginners in C; take the following fragment to study:
#define N 1000
int addHugeNumbers(char *a1, char *a2, char *res){
char resultBuffer[N];
int i1 = (int)strlen(a1);
int i2 = (int)strlen(a2);
int carryOver = 0;
int ri = 0;
while (i1 > 0 || i2 > 0) { // until both inputs have been read to their beginning
i1--;
i2--;
// read single digits and consider that a string might have already
// been read to its beginning
int d1 = i1 >= 0 ? a1[i1] - '0' : 0;
int d2 = i2 >= 0 ? a2[i2] - '0' : 0;
// check for invalid input
if (d1 < 0 || d1 > 9 || d2 < 0 || d2 > 9) {
return 0;
}
// calculate result digit, taking previous carryOver into account
int digitSum = d1 + d2 + carryOver;
carryOver = digitSum / 10;
digitSum %= 10;
resultBuffer[ri++] = digitSum + '0';
}
// write the last carryOver, if any
if (carryOver > 0) {
resultBuffer[ri++] = carryOver + '0';
}
// copy resultBuffer into res in reverse order:
while(ri--) {
*res++ = resultBuffer[ri];
}
// terminate res-string
*res = '\0';
return 1;
}
int main(int argc, char *argv[]) {
char res[N];
char a1[N/2] = "123412341234";
char a2[N/2] = "1231";
if (addHugeNumbers(a1, a2, res)) {
printf("result: %s\n", res);
} else {
printf("invalid number.\n");
}
return 0;
}

Replace a character in char* for double value

I have a char * and I need to replace a character for a double value (unknown number of digits). So I believe that I need to count the number of digits then make a realloc() and replace the characters. I'm just not sure how to count the number of digits and make this replacement.
For example:
char *c = strdup("a+b");
double d = 10;
//I'd like to replace 'a' for 10.
//then 'c' would be : 10+b.
//Next iteration I need to change the 'b' value then I get:
//c = 10 + 3
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
const char* s = "a+b";
double d = 10.23142;
//determine the string length needed for printing d
size_t n = snprintf(NULL, 0, "%g", d);
// old string length + n - 1 for replaced b + 1 for '\0'
char* new_s = malloc( strlen(s) + n - 1 + 1);
//write the double
sprintf(new_s, "%g", d);
//skip the first byte (where b is) at the source and the double's length at the destination
strcpy(new_s + n, s + 1);
printf("%s\n", new_s); //prints 10.2314+b
free(new_s);
return 0;
}
It's easy to make an off-by-one error in this kind of pointer arithmetic, so something like gcc's mudflap or AddressSanitizer are really useful in checking to make sure the program doesn't go into undefined behavior in some place.
Better yet, if you can, use C++ and you won't have to worry about this kind of stuff:
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
string s = "a+b";
double d = 10.23142;
s.replace(0,1,to_string(d));
cout<<s<<endl;
return EXIT_SUCCESS;
}
The problem with realloc is that you may not necessarily get back the same address, so technically you may not so much replace the character as create a new string.
You could measure the size of double by printing it into a static buffer, and taking strlen:
char buf[32];
sprintf(buf, "%f", dbl);
size_t Len = strlen(buf);
Demo
Now you can allocate more space, move the content to the back, and then copy characters from buf into reallocated space.

Unable to create more than 1000 files (C++ program)

I have been trying to make a program in C/C++ that creates files until the process is stopped.
The file names start from 0 and follow an arithmetic sequence.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
long cifre(long x) //returns the number of digits a number has
{
int nr = 0;
while (x != 0)
{
x = x/10;
nr++;
}
return nr;
}
int main()
{
long i=0;
FILE* g;
char* v;
char buffer[1025];
int j=0;
for (j=0;j<1024;j++)
buffer[j] = 'x';
while (1)
{
v = (char*)malloc(cifre(i)+10);
snprintf(v,sizeof(v),"%ld",i);
g = fopen(v,"w");
fprintf(g,"%s",buffer);
free(v);
fclose(g);
i++;
}
return 0;
}
The problem is that the program creates only 1000 files.
The sizeof(v) in the call to sprintf is the size of a char pointer, in your case probably 4, which means that the formatted string will contain at most 3 characters, or the numbers from 0 to 999. To fix this use the same length you used to allocate memory:
size_t len = cifre(i)+10;
v = (char*)malloc(len);
snprintf(v,len,"%ld",i);
snprintf(v,sizeof(v) doesn't make much sense because sizeof(v) returns the size of the pointer (v is char*), not the size of the dynamically allocated array. And so snprintf() is limited to only printing sizeof(v)-1 characters, or 3 digits and the NUL string terminator. 3 digits give you values from 000 to 999, exactly 1000.

Grab all integers from irregular strings in C

I am looking for a (relatively) simple way to parse a random string and extract all of the integers from it and put them into an Array - this differs from some of the other questions which are similar because my strings have no standard format.
Example:
pt112parah salin10n m5:isstupid::42$%&%^*%7first3
I would need to eventually get an array with these contents:
112 10 5 42 7 3
And I would like a method more efficient then going character by character through a string.
Thanks for your help
A quick solution. I'm assuming that there are no numbers that exceed the range of long, and that there are no minus signs to worry about. If those are problems, then you need to do a lot more work analyzing the results of strtol() and you need to detect '-' followed by a digit.
The code does loop over all characters; I don't think you can avoid that. But it does use strtol() to process each sequence of digits (once the first digit is found), and resumes where strtol() left off (and strtol() is kind enough to tell us exactly where it stopped its conversion).
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
int main(void)
{
const char data[] = "pt112parah salin10n m5:isstupid::42$%&%^*%7first3";
long results[100];
int nresult = 0;
const char *s = data;
char c;
while ((c = *s++) != '\0')
{
if (isdigit(c))
{
char *end;
results[nresult++] = strtol(s-1, &end, 10);
s = end;
}
}
for (int i = 0; i < nresult; i++)
printf("%d: %ld\n", i, results[i]);
return 0;
}
Output:
0: 112
1: 10
2: 5
3: 42
4: 7
5: 3
More efficient than going through character by character?
Not possible, because you must look at every character to know that it is not an integer.
Now, given that you have to go though the string character by character, I would recommend simply casting each character as an int and checking that:
//string tmp = ""; declared outside of loop.
//pseudocode for inner loop:
int intVal = (int)c;
if(intVal >=48 && intVal <= 57){ //0-9 are 48-57 when char casted to int.
tmp += c;
}
else if(tmp.length > 0){
array[?] = (int)tmp; // ? is where to add the int to the array.
tmp = "";
}
array will contain your solution.
Just because I've been writing Python all day and I want a break. Declaring an array will be tricky. Either you have to run it twice to work out how many numbers you have (and then allocate the array) or just use the numbers one by one as in this example.
NB the ASCII characters for '0' to '9' are 48 to 57 (i.e. consecutive).
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
int main(int argc, char **argv)
{
char *input = "pt112par0ah salin10n m5:isstupid::42$%&%^*%7first3";
int length = strlen(input);
int value = 0;
int i;
bool gotnumber = false;
for (i = 0; i < length; i++)
{
if (input[i] >= '0' && input[i] <= '9')
{
gotnumber = true;
value = value * 10; // shift up a column
value += input[i] - '0'; // casting the char to an int
}
else if (gotnumber) // we hit this the first time we encounter a non-number after we've had numbers
{
printf("Value: %d \n", value);
value = 0;
gotnumber = false;
}
}
return 0;
}
EDIT: the previous verison didn't deal with 0
Another solution is to use the strtok function
/* strtok example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] = "pt112parah salin10n m5:isstupid::42$%&%^*%7first3";
char * pch;
printf ("Splitting string \"%s\" into tokens:\n",str);
pch = strtok (str," abcdefghijklmnopqrstuvwxyz:$%&^*");
while (pch != NULL)
{
printf ("%s\n",pch);
pch = strtok (NULL, " abcdefghijklmnopqrstuvwxyz:$%&^*");
}
return 0;
}
Gives:
112
10
5
42
7
3
Perhaps not the best solution for this task, since you need to specify all characters that will be treated as a token. But it is an alternative to the other solutions.
And if you don't mind using C++ instead of C (usually there isn't a good reason why not), then you can reduce your solution to just two lines of code (using AXE parser generator):
vector<int> numbers;
auto number_rule = *(*(axe::r_any() - axe::r_num())
& *axe::r_num() >> axe::e_push_back(numbers));
now test it:
std::string str = "pt112parah salin10n m5:isstupid::42$%&%^*%7first3";
number_rule(str.begin(), str.end());
std::for_each(numbers.begin(), numbers.end(), [](int i) { std::cout << "\ni=" << i; });
and sure enough, you got your numbers back.
And as a bonus, you don't need to change anything when parsing unicode wide strings:
std::wstring str = L"pt112parah salin10n m5:isstupid::42$%&%^*%7first3";
number_rule(str.begin(), str.end());
std::for_each(numbers.begin(), numbers.end(), [](int i) { std::cout << "\ni=" << i; });
and sure enough, you got the same numbers back.
#include <stdio.h>
#include <string.h>
#include <math.h>
int main(void)
{
char *input = "pt112par0ah salin10n m5:isstupid::42$%&%^*%7first3";
char *pos = input;
int integers[strlen(input) / 2]; // The maximum possible number of integers is half the length of the string, due to the smallest number of digits possible per integer being 1 and the smallest number of characters between two different integers also being 1
unsigned int numInts= 0;
while ((pos = strpbrk(pos, "0123456789")) != NULL) // strpbrk() prototype in string.h
{
sscanf(pos, "%u", &(integers[numInts]));
if (integers[numInts] == 0)
pos++;
else
pos += (int) log10(integers[numInts]) + 1; // requires math.h
numInts++;
}
for (int i = 0; i < numInts; i++)
printf("%d ", integers[i]);
return 0;
}
Finding the integers is accomplished via repeated calls to strpbrk() on the offset pointer, with the pointer being offset again by an amount equaling the number of digits in the integer, calculated by finding the base-10 logarithm of the integer and adding 1 (with a special case for when the integer is 0). No need to use abs() on the integer when calculating the logarithm, as you stated the integers will be non-negative. If you wanted to be more space-efficient, you could use unsigned char integers[] rather than int integers[], as you stated the integers will all be <256, but that isn't a necessity.

Resources