I am writing an encoding function, that takes a file descriptor from the source file, and a FILE* as the destination file. If the input file has this: AABBBccccthen I should write 2A3B4c in the output file. (number of the same consecutive character).
I managed to do this but my only problem is the number of occurrence for the first letter gets a +1... So I would get : 3A3B4c. The function returns the total number of characters written in outt.
int encode_aux(int fd1, char *buffer, FILE *outt)
{
size_t c = read(fd1, buffer, sizeof(buffer) - 1);
char previous; //to check if the next character is the same
int count = 0; //number of occurence of the same character
int total = 0; //total number of chars written in the output file
while (c != 0)
{
for (size_t i = 0; i < c; i++)
{
if (count == 0)
{
previous = buffer[i];
count += 1;
}
if (count != 0)
{
if (previous == buffer[i])
{
count += 1;
}
else
{
if (i == 0)
{
count -= 1;
}
if (count != 1)
{
total += fprintf(outt, "%d", count);
}
total += fprintf(outt, "%c", previous);
previous = buffer[i];
count = 1;
}
}
}
buffer[c] = '\0';
c = read(fd1, buffer, sizeof(buffer) - 1);
}
return total;
}
int encode(const char *file_in, const char *file_out)
{
FILE *out = fopen(file_out, "w");
char buff[4096];
int fd = open(file_in, O_RDONLY);
if (fd == -1 || out == NULL)
{
return -1;
}
int tot = encode_aux(fd, buff, out);
if (close(fd) == -1 || fclose(out) != 0)
{
return -1;
}
return tot;
}
if (count == 0)
{
previous = buffer[i];
count += 1;
}
if (count != 0)
{
if (previous == buffer[i])
{
count += 1;
}
When count == 0 then count = 1. Then you check count != 0, cause you just did count += 1, because count was 0. Then if (previous == buffer[i]), which also will be always true, because you just did previous = buffer[i] int he first if. Then you increment count again.
I think you miss an else:
if (count == 0)
{
previous = buffer[i];
count += 1;
} else
if (count != 0)
{
if (previous == buffer[i])
{
count += 1;
}
Notes:
I think you are mixing fgets with read calls. The buffer[c] = '\0'; is unneeded, the read call just reads bytes from the file descriptors, whatever they are, and if c will be too bug for buffer, it will be out-of-bounds access.
You are not printing the count of the last character of the file. It just so happens, that your file ends with a newline - the previous is set to newline on the last execution of your loop. If your file wouldn't end with w newline, the count of the last character won't be printed.
Related
I am trying to take a list of MAC Addresses and Device names from a text file and store them in an array called list;
struct list {
char ch;
char a[2], b[2], c[2], d[2], e[2], f[2], g[2], alias[32]; //chars a-g are supposed to be a maximum of two characters long to store the different segments of the mac addresses, and the alias stores the device name up to 32 characters.
};
The main function here, as of right now is supposed to open the file "Inet.txt" and read each character individually using "char cur = fgetc." The function then assigns the different parts of the MAC address to its corresponding position in chars a-g of the list struct, and the alias char if the function goes more than 2 chars without reaching a ":" or a " ". The length of the current char is represented by the variable k, which increases every time the program detects a letter or a number, and is reset to -1 every time variable 'cur' is assigned to something. There is also an array "struct list *head[32]; " which stores each line separately, the line number being identified by the variable "int i", which increases by one every time "cur == '\n'" starting at "int = 0." The main function is as follows;
int main()
{
FILE *fp;
char cur, temp[32], temp2[32], p;
struct list *head[32];
head[0]=(struct list*)malloc(sizeof(struct list));
int num = 0, d, data, devices, i = 0, j = -1, k = -1, l = 0;
char arr[100][2];
int count = 0;
//head = current = NULL;
fp = fopen("Inet.txt", "r");
if(fp==NULL)
{
printf("Error opening file.");
}
while((cur = fgetc(fp)) != EOF)
{
//stringchecker(cur)!=0
if((cur >= 48 && cur <= 57)||(cur >= 97 && cur <= 122)||(cur >= 65 && cur <= 90))
{
k++; //counter for temp array size
if(cur >= 97 && cur <= 122)
{
temp[k] = cur-32;
}
else
{
temp[k] = cur;
}
if(k>1)
{
strncpy(temp2, temp, k+1);
temp2[k+1] = '\0';
aloc(&head[i],temp2,7);
// k = -1;
}
}
else if(cur == ':')
{
if(count == 0)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
else if(count == 1)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
else if(count == 2)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
else if(count == 3)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
else if(count == 4)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
else if(count == 5)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
count++;
k = -1;
}
else if(cur == ' ')
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,6);
k = -1;
}
else if(cur == '\n')
{
printf("\n%s:%s:%s:%s:%s%s\nALIAS: %s", (*head[i]).a,(*head[i]).b,(*head[i]).c,(*head[i]).d,(*head[i]).e,(*head[i]).f,(*head[i]).alias);
exit(0);
devices++;
data++;
count = 0;
num = -1;
i++;
j = -1;
k = -1;
head[i]=(struct list*)malloc(sizeof(struct list));
//exit(0);
}
}
fclose(fp);
return 0;
}
The "aloc()" function assigns the current char up to 16 characters to a-g or alias depending on the value of the variable count, which is a parameter of this function. The aloc() function is as follows;
void aloc(struct list **head, char ch[16], int count) //assigns ch value to specific variable of the current head based on the value of count 1-7
{
if(count == 0)
{
strncpy((*head)->a,ch, 2);
}
else if(count == 1)
{
strncpy((*head)->b,ch, 2);
}
else if(count == 2)
{
strncpy((*head)->c,ch, 2);
}
else if(count == 3)
{
strncpy((*head)->d,ch, 2);
}
else if(count == 4)
{
strncpy((*head)->e,ch, 2);
}
else if(count == 5)
{
strncpy((*head)->f,ch, 2);
}
else if(count == 6)
{
strncpy((*head)->g,ch, 2);
}
else if(count == 7)
{
strncpy((*head)->alias,ch, 16);
}
}
The input text file "Inet.txt" is as follows;
A0:FB:C5:44:b8:45 PLATTE
58:24:29:0f:c8:ee JET
F1:C0:11:16:53:1F Wabash
A0:FB:C5:32:15:10 GREEN
33:68:29:a1:b2:3c Charlie
58:24:29:0A:0B:C0 BAKER
GG:01:X0:99:1A:45 FOXTROT
The main problem I am having with this code is the variables a-g are not being assigned correctly. When I run the program to only read the first line, I get the following output:
A0FBC544B8:FBC544B8:C544B8:44B8:B8
ALIAS: PLATTE%
When the output should be:
A0:FB:C5:44:B8
ALIAS: PLATTE
I am not sure which line is causing the entire mac address to be assigned to char a of the current list. I will post the code as I have it in its entirety here to avoid confusion.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
struct list {
char ch;
char a[2], b[2], c[2], d[2], e[2], f[2], g[2], alias[32];
};
void aloc(struct list **head, char ch[16], int count)
{
if(count == 0)
{
strncpy((*head)->a,ch, 2);
}
else if(count == 1)
{
strncpy((*head)->b,ch, 2);
}
else if(count == 2)
{
strncpy((*head)->c,ch, 2);
}
else if(count == 3)
{
strncpy((*head)->d,ch, 2);
}
else if(count == 4)
{
strncpy((*head)->e,ch, 2);
}
else if(count == 5)
{
strncpy((*head)->f,ch, 2);
}
else if(count == 6)
{
strncpy((*head)->g,ch, 2);
}
else if(count == 7)
{
strncpy((*head)->alias,ch, 16);
}
}
int main()
{
FILE *fp;
char cur, temp[32], temp2[32], p;
struct list *head[32];
head[0]=(struct list*)malloc(sizeof(struct list));
int num = 0, d, data, devices, i = 0, j = -1, k = -1, l = 0;
char arr[100][2];
int count = 0;
//head = current = NULL;
fp = fopen("Inet.txt", "r");
if(fp==NULL)
{
printf("Error opening file.");
}
while((cur = fgetc(fp)) != EOF)
{
//stringchecker(cur)!=0
if((cur >= 48 && cur <= 57)||(cur >= 97 && cur <= 122)||(cur >= 65 && cur <= 90))
{
k++; //counter for temp array size
if(cur >= 97 && cur <= 122)
{
temp[k] = cur-32;
}
else
{
temp[k] = cur;
}
if(k>1)
{
strncpy(temp2, temp, k+1);
temp2[k+1] = '\0';
aloc(&head[i],temp2,7);
// k = -1;
}
}
else if(cur == ':')
{
if(count == 0)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
else if(count == 1)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
else if(count == 2)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
else if(count == 3)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
else if(count == 4)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
else if(count == 5)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
count++;
k = -1;
}
else if(cur == ' ')
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,6);
k = -1;
}
else if(cur == '\n')
{
printf("\n%s:%s:%s:%s:%s%s\nALIAS: %s", (*head[i]).a,(*head[i]).b,(*head[i]).c,(*head[i]).d,(*head[i]).e,(*head[i]).f,(*head[i]).alias);
exit(0);
devices++;
data++;
count = 0;
num = -1;
i++;
j = -1;
k = -1;
head[i]=(struct list*)malloc(sizeof(struct list));
//exit(0);
}
}
fclose(fp);
return 0;
}
I initially tried writing this program using linked lists, but I thought it would be easier to keep track of an array of list structs for use later in my program. However I keep getting the same problem with my output. Any help is appreciated.
If you remove exit(0); from the block here
else if(cur == '\n')
{
printf(/* ... */);
exit(0);
devices++;
data++;
count = 0;
/* ... */
then this program appears to work1.
I say "appears" because this program invokes Undefined Behaviour by printing non null-terminated buffers with the printf specifier %s.
You need to either specify a precision, being the maximum number of bytes to print, with each %s specifier. For example:
#include <stdio.h>
int main(void)
{
char buf[2] = "AB"; /* the null byte is not stored */
printf("%2s\n", buf);
}
Or, you need to ensure your buffers are large enough to store a desired string length plus the null-terminating byte. If you want to store a string of length 2, your buffer must be at least of size 3.
#include <stdio.h>
int main(void)
{
char buf[3] = "AB"; /* the null byte IS stored */
printf("%s\n", buf);
}
Note that strncpy is notoriously hard to use, as it does not null-terminate the buffer if the length of the source string is greater than or equal to the size provided.
1. You must also change char cur to int cur. On platforms when char is an unsigned type, you will not be able to reliably test against the negative int value of EOF. fgetc returns an int for this reason.
As pointed out in the comments, avoid magic numbers and instead use the functions found in <ctype.h>.
If your file contents are predictably formatted, you can just use fgets + sscanf to read each line. For example:
#include <stdio.h>
#include <stdlib.h>
#define MAX_ADDRS 256
struct address {
char a[3];
char b[3];
char c[3];
char d[3];
char e[3];
char f[3];
char alias[32];
};
size_t read_macs(struct address *addrs, size_t limit, FILE *f)
{
char buffer[512];
size_t n = 0;
while (n < limit && fgets(buffer, sizeof buffer, f)) {
int cv = sscanf(buffer, "%2s:%2s:%2s:%2s:%2s:%2s%31s",
addrs[n].a, addrs[n].b, addrs[n].c,
addrs[n].d, addrs[n].e, addrs[n].f,
addrs[n].alias);
if (7 == cv)
n++;
}
return n;
}
int main(int argc, char **argv)
{
if (argc < 2) {
fprintf(stderr, "usage: %s FILENAME\n", *argv);
return EXIT_FAILURE;
}
FILE *file = fopen(argv[1], "r");
if (!file) {
perror(argv[1]);
return EXIT_FAILURE;
}
struct address store[MAX_ADDRS];
size_t length = read_macs(store, MAX_ADDRS, file);
fclose(file);
for (size_t i = 0; i < length; i++)
printf("%s (%s:....:%s)\n",
store[i].alias, store[i].a, store[i].f);
}
$ ./a.out Inet.txt
PLATTE (A0:....:45)
JET (58:....:ee)
Wabash (F1:....:1F)
GREEN (A0:....:10)
Charlie (33:....:3c)
BAKER (58:....:C0)
FOXTROT (GG:....:45)
I received an assignment to write a code that would erase the instances of a string in another string, and although my code does that successfully, the symbol ╠ appears many times at the end of the result string.
Example:
For input string 1 - A string is a string, and an input string 2 - str
The result should be A ing is a ing.
But I receive A ing is a ing╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
Hoped I could get some assistance regarding this issue, cause no matter what I've tried I wasn't able to
fix this.
#include <stdio.h>
#define STRING_SIZE 100
int StrippingFunc(char input_str1[STRING_SIZE], char input_str2[STRING_SIZE], char
result_string[STRING_SIZE])
{
if (input_str2[0] == '\n' || input_str2[0] == '\0')
{
return 0;
}
for (int k1 = 0; k1 < STRING_SIZE; k1++)
{
if (input_str1[k1] == '\n')
{
input_str1[k1] = '\0';
}
}
for (int k2 = 0; k2 < STRING_SIZE; k2++)
{
if (input_str2[k2] == '\n')
{
input_str2[k2] = '\0';
}
}
int Length;
int length2 = 0;
int index2 = 0;
while (input_str2[index2] != '\0') // Loop used to determine input_string2's length.
{
length2++;
index2++;
}
int InString = 0;
int i = 0;
int j;
int resultindex = 0;
while (input_str1[i] != '\0')
{
Length = length2;
int l = i;
j = 0;
int proceed = 1;
if (input_str1[l] == input_str2[j])
{
while ((input_str2[j] != '\0') && (proceed != 0))
{
while (Length >= 0)
{
if (Length == 0)
{
InString = 1;
i += (l-i-1);
proceed = 0;
Length = -1;
}
if (input_str1[l] == input_str2[j])
{
Length--;
j++;
l++;
}
else if ((input_str1[l-1] == input_str2[j-1]) && (input_str2[j] == '\0'))
{
proceed = 0;
Length = -1;
}
else
{
proceed = 0;
Length = -1;
result_string[resultindex] = input_str1[l - 1];
resultindex++;
}
}
}
}
else
{
result_string[resultindex] = input_str1[i];
resultindex++;
}
i++;
}
return InString;
}
int main()
{
char result_string[STRING_SIZE];
char input_string1[STRING_SIZE];
char input_string2[STRING_SIZE];
printf("Please enter the main string..\n");
// Your function call here..
fgets(input_string1, STRING_SIZE + 1, stdin);
printf("Please enter the pattern string to find..\n");
// Your function call here..
fgets(input_string2, STRING_SIZE + 1, stdin);
int is_stripped = StrippingFunc(input_string1, input_string2, result_string);; // Your function call here..
// Store the result in the result_string if it exists
printf("> ");
printf(is_stripped ? result_string : "Cannot find the pattern in the string!");
return 0;
}
But I receive A ing is a ing╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
In the code after you fill result_string but you missed to add the final null character, because of that the printf after reach non initialized characters with an undefined behavior producing your unexpected writting. After
while (input_str1[i] != '\0')
{
Length = length2;
...
}
add
result_string[resultindex] = 0;
note you have the place for because result_string and input_str1 have the same size
Having
char input_string1[STRING_SIZE];
char input_string2[STRING_SIZE];
these two lines can have an undefined behavior :
fgets(input_string1, STRING_SIZE + 1, stdin);
fgets(input_string2, STRING_SIZE + 1, stdin);
because fgets may write after the end of the arrays, you need to remove +1 or to size the arrays one more
In
for (int k1 = 0; k1 < STRING_SIZE; k1++)
{
if (input_str1[k1] == '\n')
{
input_str1[k1] = '\0';
}
}
for (int k2 = 0; k2 < STRING_SIZE; k2++)
{
if (input_str2[k2] == '\n')
{
input_str2[k2] = '\0';
}
}
except if fgets fill all the arrays you have an undefined behavior working on non initialized characters because you do not stop when you reach newline or the null character.
In
int length2 = 0;
int index2 = 0;
while (input_str2[index2] != '\0') // Loop used to determine input_string2's length.
{
length2++;
index2++;
}
length2 and length2 have exactly the same value, is it useless to have two variables, and in fact this lop is useless because the previous loop with the right termination already give you the expected length.
In
printf(is_stripped ? result_string : "Cannot find the pattern in the string!");
I encourage you to replace printf by a puts not only to add a final newline to flush the output and make it more clear in case you start your program in a shell, but also because in case the input string contains for instance %d and it is not removed and is_stripped is true then printf will try to get an argument whose do not exist, with an undefined behavior
If you do all the corrections with your inputs your code will print > A ing is a ing without undefined behavior
I have a large text file. In this file there are some numbers I want to add together.
What I've tried:
int sum = 0, i = 0;
file = fopen(filename, "r");
while ((i = fgetc(file)) != EOF) {
if (isdigit(i)) {
sum++;
}
}
printf("Sum of numbers is: %i", sum);
fclose(file);
But that isdigit(i) is just a counter of how many digits this file contains, not what the sum of the numbers is.
The input is: "This text 15 is very 19 nice."
The result should be: Sum of numbers is: 34
Consider the decimal placement
The missing part in the question's code is accumulating the digits (as opposed to counting the digits with sum++;) AND multiplying by ten the previous accumulated number before adding the next digit.
The answer is in:
number = number * 10 + i - '0';
The - '0' part is converting ASCII digit to a number.
Everything else in the below code is checks to make sure there are no obvious overflows and correctly supporting minus sign also adjacent to the numbers, as well as ignoring digits after the decimal point(s). I'm sure it is not perfect, but the idea here is to provide a working example of how it could be done, rather than a well tested code and using a library call to do it for you.
By popular demand (comments were deleted now) I've added a simple-but-working overflow check:
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <error.h>
#include <limits.h>
int main(int argc, char* argv[]) {
int sum = 0, state = 0, i = 0, dir = 1;
unsigned int number = 0, check;
if (argc < 2) {
fprintf(stderr, "Missing filename\n");
return EXIT_FAILURE;
}
char* filename = argv[1];
FILE* file = fopen(filename, "r");
if (!file) {
perror(filename);
return EXIT_FAILURE;
}
while (i != EOF) {
i = fgetc(file);
if (isdigit(i)) {
if (dir) {
state = 1;
check = number;
number = number * 10 + i - '0';
if (check > number || number > INT_MAX) {
fprintf(stderr, "Single number overflow error\n");
fclose(file);
return EXIT_FAILURE;
}
}
} else {
if (state && dir) {
check = number;
if (dir < 0 && sum < 0)
check -= sum;
else if (dir > 0 && sum > 0)
check += sum;
if (check > INT_MAX) {
fprintf(stderr, "Sum overflow error\n");
fclose(file);
return EXIT_FAILURE;
}
sum += number * dir;
number = 0;
}
state = 0;
dir = i == '-' ? -1 : i == '.' ? 0 : 1;
}
}
printf("Sum of numbers is: %i\n", sum);
fclose(file);
return EXIT_SUCCESS;
}
Test run:
$ cat opString.txt
This text 15 is very 19 nice.
$ ./test2 opString.txt
Sum of numbers is: 34
$
And just in case you are on 64bit linux system, and need much higher performance (you mentioned large file) the below code will map the entire file (even file larger than memory, the kernel will handle it nicely) and will not make a library call on every char. In my tests, isdigit() and strtol() slows it down significantly.
#include <stdlib.h>
#include <stdio.h>
#include <error.h>
#include <limits.h>
#include <sys/mman.h>
int addToSum(unsigned int* number, int* sum, int dir, FILE* file) {
unsigned int check;
check = *number;
if (dir < 0 && *sum < 0)
check -= *sum;
else if (dir > 0 && *sum > 0)
check += *sum;
if (check > INT_MAX) {
fprintf(stderr, "Sum overflow error\n");
fclose(file);
exit(EXIT_FAILURE);
}
*sum += *number * dir;
*number = 0;
}
int main(int argc, char* argv[]) {
int sum = 0, state = 0, i = 0, dir = 1;
unsigned int number = 0, check;
if (argc < 2) {
fprintf(stderr, "Missing filename\n");
return EXIT_FAILURE;
}
char* filename = argv[1];
FILE* file = fopen(filename, "r");
if (!file) {
perror(filename);
return EXIT_FAILURE;
}
if (fseek(file, 0L, SEEK_END) < 0) {
perror("fseek failed");
fclose(file);
return EXIT_FAILURE;
}
long fsize = ftell(file);
char* fmap = mmap(NULL, fsize, PROT_READ, MAP_SHARED, fileno(file), 0);
if (fmap == MAP_FAILED) {
perror("map failed");
fclose(file);
return EXIT_FAILURE;
}
long pos = 0;
while (pos < fsize) {
i = fmap[pos++];
if (i >= '0' && i <= '9') {
if (dir) {
state = 1;
check = number;
number = number * 10 + i - '0';
if (check > number || number > INT_MAX) {
fprintf(stderr, "Single number overflow error\n");
fclose(file);
return EXIT_FAILURE;
}
}
} else {
if (state && dir) addToSum(&number, &sum, dir, file);
state = 0;
dir = i == '-' ? -1 : i == '.' ? 0 : 1;
}
}
if (state && dir) addToSum(&number, &sum, dir, file);
printf("Sum of numbers is: %i\n", sum);
fclose(file);
return EXIT_SUCCESS;
}
Input file name: file.txt
This text 15 is very 19 nice.
Output:
Sum of numbers is: 34
Code:
#include <stdio.h>
#include <ctype.h>
int main()
{
int sum = 0, num, i = 0;
int state = 0;
FILE* f;
if ((f = fopen("file.txt", "r")) == NULL)
return -1;
while ((i = fgetc(f)) != EOF) {
switch(state) {
case 0: // processing text
if (isdigit(i)) {
num = i - '0';
state = 1;
}
break;
case 1: // processing number
if (isdigit(i)) {
num *= 10;
num += i - '0';
}
else {
sum += num;
num = 0;
state = 0;
}
break;
}
}
if (state == 1) {
sum += num;
}
printf("Sum of numbers is: %i\n", sum);
fclose(f);
return 0;
}
But that isdigit(i) is just a counter of how many digits this text contents but not whats the sum of the numbers is.
Keep in mind, the function isdigit() reads one character per call. So if it reads the character 9 for example, the value sum should grow by i - '0' (or 57 - 48, or 9). And if there were two characters in sequence, such as 92, reading one character at a time, the value sum would likewise be incremented by 9+2 -> 11, not 92. Given this is what you want, here is how to do it:
The values you have determined are digits are actually ASCII values, so in viewing this table you can see all of the digits will have values from '0' to '9' (or in ASCII, 48 to 57). So in your code you can simply change one line to sum up the value count:
int sum = 0;
file = fopen(filename, "r");
while ((i = fgetc(file)) != EOF) {
if (isdigit(i))
sum += (i - '0');//subtract 48 from every 'i' verified as digit
//Sum will therefore add up values
//between (48-48) to (57-48)
//(or between 0 to 9)
}
printf(f,"Sum of numbers is: %i", sum);
fclose(file);
However, If you want to sum up numeric values represented by a sequence of digits within a buffer, then the code is different. It would require keeping a flag as the buffer content is read.
In pseudo code:
char accumlator[10] = {0}; max possible sequential digits (change as needed)
int found = 0;//flag
int sum = 0;
while ((i = fgetc(file)) != EOF)
{
if (isdigit(i))
{
accumulator[found] = i;
found++;
}
else
{
if(found != 0)
{
sum += atoi(accumulator);
found = 0;
accumulator[0] = 0;
}
}
}
}
The function fgetc only reads a single character that is a potential digit. It does not read a whole number that consist of several digits at once. If you want to read whole numbers, you must change the logic of your program. You can do one of the following:
Use fgetc as you do now and make your program assemble the individual digits into a number, by using the ASCII Code of the digits and converting that to a number using arithmetic. The numerical value of an individual ASCII character digit can be obtained using the expression ascii_code - '0'.
Use fgets instead to read a whole line at once and then use strtol to convert any digits you find into numbers.
Use fscanf() to parse the file and extract the number in one step (not recommended unless you know exactly what you are doing).
Your program only counts the number of digits in the file. Here is a simple solution using getc() to compute the number of all integral numbers in the file without the need for a state machine:
#include <stdio.h>
int count_numbers(const char *filename) {
FILE *fp;
unsigned long long total = 0, current = 0;
int c;
if ((fp = fopen(filename, "r")) == NULL) {
printf("Cannot open input file %s\n", filename);
return -1;
}
while ((c = getc(fp)) != EOF) {
if (c >= '0' && c <= '9') {
current = current * 10 + (c - '0');
} else {
total += current;
current = 0;
}
}
total += current; /* add the last number if at the very end of the file */
printf("Sum of numbers is: %llu\n", total);
fclose(fp);
return 0;
}
Notes:
this program does not handle negative numbers, it ignores negative signs.
it does not handle floating point numbers: processing 1.1 will produce a sum of 2.
very long numbers will produce the result modulo ULLONG_MAX+1 but at least the behavior is defined.
If you want to add all the number strings up in this then you'll have to search for the digits and then convert them from a string to an integer using strtol. Here's how to do that:
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#define OVERFLOW_STR "Number cannot be represented in an int!\n"
int main(int argc, char **argv)
{
FILE *file;
char n[12];
char *p = n;
int i = 0, sum = 0, tmp, old_errno;
if(argc != 2)
{
fprintf(stderr, "Usage: %s file\n", argv[0]);
return EXIT_FAILURE;
}
file = fopen(argv[1], "r");
if(!file)
{
perror("Error opening file");
return EXIT_FAILURE;
}
while(i != EOF)
{
while( (i = fgetc(file)) != EOF &&
i != '-' &&
i != '+' &&
!isdigit(i) )
{
/* empty loop */ ;
}
if(i != EOF)
{
do {
if( p == n + sizeof(n) - 1 ||
((*n != '-' || *n != '+') &&
p == n + sizeof(n) - 2) )
{
fprintf(stderr, "Error: " OVERFLOW_STR);
return EXIT_FAILURE;
}
*p++ = i;
} while( (i = fgetc(file)) != EOF && isdigit(i) );
}
else
break;
*p = 0;
old_errno = errno;
errno = 0;
tmp = strtol(n, NULL, 10);
if( tmp == 0 && errno != 0 )
{
perror("Error converting string");
break;
}
errno = old_errno;
sum += tmp;
p = n;
}
printf("Sum of numbers is: %d\n", sum);
fclose(file);
return 0;
}
This is largely error-checked. There may be corner cases that I have left out.
Input (contained in a file whose name is passed as a command-line parameter):
This text 15 is very 19 nice.
Output:
Sum of numbers is: 34
My reverse function dont give the last(first) character back if the file ended with character and not with "\n" (hex 0A). My system is Posix. Can anywhere help?
Output:
54
3. sor Csörnyeföl //here failed "d" the last character
2. sor Szentendre
1. sor Budapest
Here is the Code:
void rf_revers(FILE *fp) {
int i, size, start, loop, counter;
char *buffer;
char line[256];
start = 0;
fseek(fp, 0, SEEK_END);
size = ftell(fp);
printf("%d\n", size); // for test that fseek/ftell giving me the right value
buffer = malloc((size+1) * sizeof(char));
for (i=0; i< size; i++) {
fseek(fp, size-1-i, SEEK_SET);
buffer[i] = fgetc(fp);
if(buffer[i] == 10) {
if(i != 0) {
counter = 0;
for(loop = i; loop > start; loop--) {
if((counter == 0) && (buffer[loop] == 10)) {
continue;
}
line[counter] = buffer[loop];
counter++;
}
line[counter] = 0;
start = i;
printf("%s\n",line);
}
}
}
if(i > start) {
counter = 0;
for(loop = i; loop > start; loop--) {
if((counter == 0) && ((buffer[loop] == 10) || (buffer[loop] == 0))) {
continue;
}
line[counter] = buffer[loop];
counter++;
}
line[counter] = 0;
printf("%s\n",line);
return;
}
}
--[sorry for bad english]--
I thought about it, tried it out, but in time find the solution.
for(loop = i; loop >= start; loop--) { // change loop > start to >=
if(/*(counter == 0) &&*/ (buffer[loop] == 10)) {
and:
if(i > start) {
counter = 0;
for(loop = i-1; loop > start; loop--) {
I am using isalpha() to determine if there are characters on the line and it is showing that the line "90 1 0" contains alphabetic characters.
Here is the relevant code
bool digitTest(char *test, int arysize)
{
int i;
for (i =0; i<arysize; i++)
{
if ((isalpha(test[i])) != 0){
return 0;
}
if (i==arysize)
return 1;
i++;
}
return 1;
}
It is called here
char buffer[4096] = {};
while(NULL!=fgets(buffer, sizeof(buffer), fp)){
if (digitTest(buffer, 4096) == 0){
printf ("contains alpha\n");
continue;
/*printing code if there is no alphabetic characters below, i do not believe it is relevant*/
and this is output
1
1
1
contains alpha
contains alpha
contains alpha
25123.90
54321.23
6
and input
1
1
1
89 23.5 not a number
90 1 0
-5.25 not a number 10000
25123.90 54321.23 6
The code has a few problems:
You shouldn't check all buffer. Check buffer until \0. because the output of fgets is a C-style string.
Second problem is an extra i++ in function digitTest. You should remove it.
You don't need arysize any more.
Use this digitTest function instead
int digitTest(char *test)
{
int i;
for (i=0; test[i] && test[i] != '\n'; i++)
{
if (isalpha(test[i]) != 0)
return 0;
}
return 1;
}
(maybe has minor bugs, I didn't test it)
And call it like this:
while( fgets(buffer, sizeof(buffer), fp) ) {
if (!digitTest(buffer)) {
printf ("contains alpha\n");
continue;
}
}
It Looks like you might be accessing locations in memory that contain characters, Change your code to
char buffer[4096] = {};
memset(buffer, 0, 4096);
while(NULL!=fgets(buffer, sizeof(buffer), fp)){
if (digitTest(buffer, strlen(buffer)) == 0){ //get actual length of buffer
printf ("contains alpha\n");
continue;
EDIT
In order to make your code not respond to either \n or \r do
bool digitTest(char *test, int arysize)
{
int i;
for (i =0; i<arysize; i++)
{
if( test[i] == '\r' || test[i] == '\n')
return 1;
if ((isalpha(test[i])) != 0){
return 0;
}
if (i==arysize)
return 1;
}
return 1;
}