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 was trying this pattern matching method in C but whenever I give all the input, the vscode terminal waits for a while and just stops the program without any warnings/message. Can anyone point to what is wrong here?
#include <stdio.h>
#include <string.h>
int main()
{
char STR[100], PAT[100], REP[100], ANS[100];
int i, m, j, k, flag, slP, slR, len;
i = m = k = j = flag = len = 0;
printf("\nMain String: ");
gets(STR);
printf("\nPattern String: ");
gets(PAT);
slP = strlen(PAT);
printf("\nReplace String: ");
gets(REP);
slR = strlen(REP);
while (STR[i] != '\0')
{
if (STR[i] = PAT[j])
{
len = 0;
for (k = 0; k < slP; k++)
{
if (STR[k] = PAT[k])
len++;
}
if (len == slP)
{
flag = 1;
for (k = 0; k < slR; k++, m++)
ANS[m] = REP[k];
}
}
else
{
ANS[m] = STR[i];
m++;
i++;
}
}
if (flag == 0)
{
printf("\nPattern not found!");
}
else
{
ANS[m] = '\0';
printf("\nResultant String: %s\n", ANS);
}
return 0;
}
There are multiple problems in the code:
using gets() is risky, this function was removed from the C Standard because it cannot be used safely.
if (STR[i] = PAT[j]) copied the pattern to the string. You should use:
if (STR[i] == PAT[j])
similarly, if (STR[k] = PAT[k]) is incorrect. You should compare PAT[k] and STR[i + k]:
if (STR[i + k] == PAT[k])
you should test for buffer overflow for the output string as replacing a short string by a larger one may produce a string that will not fit in ANS
you do not increment i properly.
Here is a modified version:
#include <stdio.h>
int getstr(const char *prompt, char *dest, int size) {
int c, len = 0;
printf("%s", prompt);
while ((c = getchar()) != EOF && c != '\n') {
if (len + 1 < size)
dest[len++] = c;
}
if (size > 0)
dest[len] = '\0';
printf("\n");
if (c == EOF && len == 0)
return -1;
else
return len;
}
int main() {
char STR[100], PAT[100], REP[100], ANS[100];
int i, m, k, flag;
if (getstr("Main String: ", STR, sizeof STR) < 0)
return 1;
if (getstr("Pattern String: ", PAT, sizeof PAT) < 0)
return 1;
if (getstr("Replace String: ", REP, sizeof REP) < 0)
return 1;
i = m = flag = 0;
while (STR[i] != '\0') {
if (STR[i] == PAT[0]) { // initial match
// compare the rest of the pattern
for (k = 1; PAT[k] != '\0' && PAT[k] == STR[i + k]; k++)
continue;
if (PAT[k] == '\0') { // complete match
flag = 1;
// copy the replacement string
for (k = 0; REP[k] != '\0'; k++) {
if (m + 1 < sizeof ANS)
ANS[m++] = REP[k];
}
i += k; // skip the matching characters
continue;
}
}
// otherwise copy a single character
if (m + 1 < sizeof ANS)
ANS[m++] = STR[i];
i++;
}
ANS[m] = '\0';
if (flag == 0) {
printf("Pattern not found!\n");
} else {
printf("Resultant String: %s\n", ANS);
}
return 0;
}
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.
I wrote the following code:
int VALUE = 10;
int counter = 0;
char c;
while (true) {
if(scanf("%c", &c) != 1) {
return NULL;
}
if (c == '\n') {
break;
}
if (counter % VALUE < VALUE - 1) {
num[counter] = c;
counter++;
} else {
char* temp = (char*) malloc((counter + MAX_VALUE)*sizeof(char));
if (temp == NULL) {
return NULL;
}
for (int i = 0; i < counter; i++) {
temp[i] = num[i];
}
free(num);
num = temp;
num[counter] = c;
counter++;
}
}
printf("counter = %d\n",counter);
It doesn't really matter what it does but I have some problem with the counter. For some reason, when I insert 9876.54321 (newline at the end), It does not enters into the if (c == '\n') { break; } block when it scanfs \n, only on the next iteration. The length of 9876.54321 is 10 but it will print 11. What is the reason? I also tried to switch to getchar() but I get the same thing.
Here:
if(scanf("%c", &c) != 1) {
%c in scanf leaves the leading and following whitespace in the input buffer.
To fix this, don't use scanf. Use getchar or getc instead. Instead of using c == '\n', use isspace(c).
Don't do char c. You can't check for EOF if you do that. Instead, use int c.
Full code sample:
int VALUE = 10;
int counter = 0;
int c;
while (true) {
c = getchar();
if (isspace(c) || c == EOF) {
break;
}
if (counter % VALUE < VALUE - 1) {
num[counter] = c;
counter++;
} else {
char* temp = (char*) malloc((counter + MAX_VALUE)*sizeof(char));
if (temp == NULL) {
return NULL;
}
for (int i = 0; i < counter; i++) {
temp[i] = num[i];
}
free(num);
num = temp;
num[counter] = c;
counter++;
}
}
printf("counter = %d\n",counter);