fgets() reads and stores a char form a line twice - c

So I have a file data.txt, with random integers written in it, for example "17 12 5 4 16".
So, I used fgets() to read this line of integers and store it in a char str []. As expected str [0] == 17, but str [1] == 7, an so on... every 2 digits the integer is stored in an element in the array, but the second digit is also stored in the following element.
Example: str [3] = 12, str [4] = 2, str[5] = ' '.
What could be done to fix this issue?
Here is my very typical code:
FILE* fp = fopen ("data.txt", "r");
int i = 0, j = 0;
int size = fileSize(fp) + 1; // Enumerate the char numbers in file
char str [size];
rewind (fp);
fgets (str, size, fp);
Here is my entire code
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
typedef struct RATIONAL
{
int top;
int bottom;
} RATIONAL;
void printArray (int arr [], int count)
{
for (int i = 0; i < count; ++i)
{
printf ("%d\n", arr[i]);
}
}
int fileSize (FILE* fp)
{
char c;
int i = 0;
while ((c = getc (fp)) != EOF)
{
i++;
}
return (i);
}
int main ()
{
RATIONAL ratArray [23]; //array to hold rational numbers
int intArray [45]; //array to hold integer values
FILE* fp = fopen ("data.txt", "r");
int i = 0, j = 0;
int size = fileSize(fp) + 1; // Enumerate the char numbers in file
char str [size];
rewind (fp);
fgets (str, size, fp);
//Convert string to array of ints
while (i < size -1)
{
if (isdigit (str [i]))
{
intArray[j++] = atoi(&str[i]);
i++;
}
else
i++;
}
printArray (intArray, intArray[0]);
Input: data.txt

What you experience is absolutely expected. The key misunderstanding is that you expect arr[N] to hold the (N+1)th integer as it was an array of integers... but it's not. It is an array of characters.
If you store integers within a char array, every digit will occupy an array location. So arr[0] will contain the most significant digit of the first number and arr[a] the least significant digit.
So, in your example, the string "17 12 5 4 16" will be stored in this way:
--------------------------------------------------------------------------------------------
| 0x31 | 0x37 | 0x20 | 0x31 | 0x32 | 0x20 | 0x35 | 0x20 | 0x34 | 0x20 | 0x31 | 0x36 | 0x00 |
--------------------------------------------------------------------------------------------
| '1' | '7' | ' ' | '1' | '2' | ' ' | '5' | ' ' | '4' | ' ' | '1' | '7' | '\0' |
--------------------------------------------------------------------------------------------
All digits are in the same array and separated by spaces. The string is closed by a string terminator '\0'.
Interesting enough, if you print the whole string with printf("%s\n", arr) you get
17 12 5 4 16
but if you try to print passing the address of the second character, with printf("%s\n", &arr[1]) you get
7 12 5 4 16
And so on. This means that C has no idea (not yet) that your intention is to store 5 integers. The string is one until the string terminator is found (the space doesn't terminate the string), so starting from the second digit will just make C print all the string except the first digit.
Since you have a string formatted in a well known way and you want an array of integers, you have to parse it to get the integers:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char arr[] = "7 12 5 4 16";
int main()
{
char * token = strtok(arr, " ");
while(token != NULL)
{
printf("Next int: %d %s\n", atoi(token)); // Here you can assign the value to your array
token = strtok(NULL, " ");
}
return 0;
}
In my demonstrative example, in which the input string was a global array instead of something read from a file like in your scenario, I used strtok() (strtok_r can be used in case a re-entrant function is required) and I got the integers using the simple atoi() (just because the input format was _under control - I recommend using strtol() for a better control of the input).

Related

Add zero padding to string in c?

The concept is very similar to this Add zero-padding to a string but it's a question from c# NOT C.
While you can add a zero padding in printf like this printf("%4d", number)
How can I have a zero padding to a string? ex:
char *file_frame = "shots";
strcat(file_frame, "%3d", number); // It's my attempt to solve it. I know it's wrong
so that I would get shots000 from file_frame
You need to use sprintf
sprintf(file_frame, "%04d", 34);
The 0 indicates what you are padding with and the 4 shows the length of the integer number.
Also you should be using mutable array as below.
char *file_frame = "shots"; --> char file_frame[100] = "shots";
First you need some space to store the string. Then you "print" into this string:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char file_name[8 + 1]; // "shots###" is 8 characters, plus 1 for end-of-string marker '\0'
int number = 23;
sprintf(file_name, "shots%03d", number);
printf("\"%s\"\n", file_name);
return EXIT_SUCCESS;
}
You can combine literal parts with formatting parts in any *printf() function.
Well, this is the workaround I did: -
char output[9];
char buffer[9];
itoa(num, output, 2);
int i=0;
for (i=0; i<(8-strlen(output));i++) {
buffer[i]='0';
}
for(int j=0;j<strlen(output);j++){
buffer[i] = output[j];
i++;
}
Input:-
num = 10
Output: -
buffer = 00001010

Not able to understand the logic of validation statement where int is added to char array

I'm working on one un document code. I have a hard time in understanding one logic.
Don't understand this logic. I need to feed in with only 4 bytes, adding int 4 with 3rd indexes of buf, example 4 + 12 = 16 in hex.
This is my buf[256]= "04 02 56 12"
some can explain why this if becomes true?
if (n < 4 || 4 + buf[3] != n)
{
fprintf(stderr, "n is:%d, length byte does not match number of bytes\n", n);
return;
}
What I want is to print I'm sending the data. But the buffer I construct seems it's not valid according to that if validation statement.
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
static void process(char *s)
{
uint8_t buf[256];
unsigned n;
unsigned long v;
char *p;
memset(buf, 0, sizeof(buf));
while (isspace(*s))
s++;
//sanity check if buf is not empty
if (!*s) {
printf("S is empty\n");
return;
}
n = 0;
while (n < sizeof(buf)) {
// convert the string into unsigned long int value to given
// base accepts only hex
v = strtoul(s, &p, 16);
if (p == s || v > 255) {
fprintf(stderr, "bad data in string for byte %u\n", n);
return;
}
s = p;
while (isspace(*s))
s++;
buf[n++] = v;
if (!*s)
break;
}
//this just prints the number of whitespaces inbetween the buffer data.
printf("N %d after while\n", n);
//printing the buffer
int i=0;
for (i = 0; i < 16; i++)
printf(" %02x", (int)buf[i]);
putchar('\n');
// Dont understand this logic. I need to feed in with only 4 bytes
// adding int with 3 indexs of buf, example 4 + a = e in hex.
if (n < 4 || 4 + buf[3] != n)
{
fprintf(stderr, "n is:%d, length byte does not match number of bytes\n", n);
return;
}
printf("Sending the data\n");
}
int main() {
char s[256] = "04 02 56 12";
printf("buf_size:%lu string:%s\n", sizeof(s), s);
process(s);
return 0;
}
the array s is passed to process.
each set of characters is converted to hex using strtol and stored in buf.
buf[3] is the fourth set of characters converted to hex, not the fourth character in the s array. In the example buf[3] is 18, 0x12. s[3] is '0'.
the fourth set of characters is the length of the message excluding the first four bytes. Not sure what is the significance of the first three bytes. Since the length byte does not include these first four bytes, four is added.
For the example array to pass the test, it needs to be "04 02 56 00" the last 00 indicates there are no more bytes to follow. The original "04 02 56 12" indicates there should be 18 sets characters for 18 more bytes to follow. 0x12 == 18.

Issues with creating a copy of an argument in C

I am converting string arguments into ints I am having an issue where the first argument is not copying and converting to a string while the second argument is working just fine. I am removing the first character of the string array and printing out the rest of the string as an int.
#include <stdio.h>
#include <string.h>
#include <math.h>
int main(int argc, char *argv[])
{
char numcpy1[sizeof(argv[1])-1];
char numcpy2[sizeof(argv[2])-1];
int i, len1, len2;
int result1=0;
int result2=0;
//Access first character for base
printf("%c \n", argv[2][0]);
printf("%c \n", argv[3][0]);
//Remove first character for number1 and number 2
if(strlen(argv[2]) > 0)
{
strcpy(numcpy1, &(argv[2][1]));
}
else
{
strcpy(numcpy1, argv[2]);
}
len1 = strlen(numcpy1);
if(strlen(argv[3]) > 0)
{
strcpy(numcpy2, &(argv[3][1]));
}
else
{
strcpy(numcpy2, argv[3]);
}
len2 = strlen(numcpy2);
//Turn remaining string characters into an int
for(i=0; i<len1; i++)
{
result1 = result1 * 10 + ( numcpy1[i] - '0' );
}
for(i=0; i<len2; i++)
{
result2 = result2 * 10 + ( numcpy2[i] - '0' );
}
printf("%d \n", result1);
printf("%d \n", result2);
return 0;
}
Output:
b
b
-4844
1010
What I want:
b
b
1000
1010
The sizeof operator does not give you the length of a string, as you seem to think it does. It tell you the size of the datatype. Since argv[2] is a char *, this evaluates to the size of this pointer, most likely 4 or 8 depending on the system.
If the string in question is longer than this value, you end up writing past the end of the array. This invokes undefined behavior, which in your case manifests as an unexpected result.
If you want the length of the string, use the strlen function instead. Also, you need to add one more byte. Strings in C are null terminated, so you need space for the null byte that marks the end of the string.
char numcpy1[strlen(argv[2])];
char numcpy2[strlen(argv[3])];
Also note that we don't need to add 1 to each of these since the first character in each string isn't copied.
You need to change
char numcpy1[sizeof(argv[1])-1];
to
char numcpy1[strlen(argv[1]) + 1];
Ditto for numcpy2
I think instead of copying the numbers you could work more easily with pointers. Thereby, you don't have to think about memory allocation and copying the values just for the sake of converting them into an integer value:
const char *numcpy1 = argv[2][0]=='\0' ? argv[2] : argv[2]+1;
...

How do I remove overflow characters or append characters in a given string in C?

I basically want to have an arbitrary length string but a fixed size output string. So if I assign x a string of 10 characters, I want to end up with a string of 5 characters at the second printf. And if x is 3 characters, I wanna add on blanks to make the string 5 characters.
Here is my code so far, but its not working because I can't even access an index, it segfaults;
int main()
{
char x[] = "d dddddddd";
printf(x);
printf("");
// separate
printf(x[1]);
return 0;
}
Thanks, I wonder whether this is possible in C, but you could also attempt it in C++
EDIT:
My 2 codes are here, they both overflow;
char first[40];
char g[] = " Level %d %d Fruit %d:%d ";
char d[41] = {0};
strncpy(d, g, 40);
if(strlen(d) < 40) {
for(i = strlen(d); i < 40; i++) {
d[i] = ' ';
}
}
n = sprintf(first, d, gol, gift, minutes, secs );
2nd strategy;
char first[40];
char b[40];
strncpy(b, g, sizeof(b));
fgets(b, 40, stdin);
n = sprintf(first, b, gol, gift, minutes, secs );
Print my n onto the game's screen, I get my text and some unknown characters following. Sorry that I can't post the whole game code because its 4000 lines long split in 10 files. I wold appreciate a completely different way of getting a string of only 40 characters from a string of more characters.
Like this:
printf("%-5.5s", x);
The format conversion %-5.5s is made up of the following:
- (flag) Pad the output on the right, if necessary
5 (field width) Output at least 5 characters
.5 (precision) Use at most 5 characters of the string
s (conversion) The corresponding argument is a char* pointing to a string.
For more information, see man sprintf
If your intention was to truncate or extend the result of a call to sprintf (which is a possible interpretation of a comment), then the answer is to use snprintf, but only if you are prepared for the truncation to be at the end of the formatted output. Here's a little toy:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char** argv) {
int limit = atoi(argv[1]);
for (int j = 2; j < argc - 1; j+=2) {
int n = atoi(argv[j]);
char buffer[limit + 1];
# format split into two for explanatory purposes:
# %d %s is the format for the output
# %*.0s takes two arguments, length and "",
# and adds length spaces to the end of the output
# Because we're using snprintf, the output is truncated.
if (snprintf(buffer, limit+1, "%d %s" "%*.0s",
n, argv[j+1],
limit, "") < 0) {
perror("snprintf");
return 1;
}
if (strlen(buffer) != limit)
fprintf(stderr, "buffer length is %zd instead of %d\n",
strlen(buffer), limit);
printf("|%s|\n", buffer);
}
return 0;
}
Test run:
$ ./snp 12 3 kangaroos 10 kangaroos 125 kangaroos \
3 koalas 14 koalas 173 koalas 1294 koalas \
12964 koalas 127347 koalas
|3 kangaroos |
|10 kangaroos|
|125 kangaroo|
|3 koalas |
|14 koalas |
|173 koalas |
|1294 koalas |
|12964 koalas|
|127347 koala|
well you can write your own function for it:
void print_five(char *s)
{
int i = 0;
char d[6] = {0};
strncpy(d, s, 5);
if(strlen(d) < 5) {
for(i = strlen(d); i < 5; i++) {
d[i] = ' ';
}
}
printf("%s", d);
}
int main(void)
{
char d[10] = "helloooo";
char m[4] = "h";
print_five(d);
printf("continued\n"); //hellocontinued
print_five(m);
printf("continued\n"); //h continued
return 1;
}
In the third printf call, if you want to display character at position 1, you should do:
printf("%c", x[1]);
To append character strings, have a look at strcat: http://www.cplusplus.com/reference/cstring/strcat/?kw=strcat
Ex:
char first[1000] = "Hello", sec[] = " world";
strcat(first, sec); /* destination, source */
But strcat is unsafe, so make your own safe strcat:
char *safecat(char *dest, const char *src, size_t siz)
{
return strncat(dest, src, siz);
}
And to read at most n characters, read about fgets, passing stdin as the stream: http://www.cplusplus.com/reference/cstdio/fgets/?kw=fgets
Ex:
char buf[MAXBUF];
fgets(buf, MAXBUF, stdin); /* this only reads MAXBUF - 1 characters. The
rest is left in stdin */

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