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 recently started learning basic C and I am still noobie with it, I started doing some projects for learning and I am using library functions but I am interested in other methods...
So I have an email validation, it works fine, but I want to do it without strlen, any suggestion what can I do instead of strlen?
void mail(char e[]) {
int count = 0;
int countb = 0;
int i, j;
int t, t2;
int p = 0;
for (i = 0; i < strlen(e); i++) {
if (e[i] == '#') {
count++;
t = i;
}
}
if (count == 1) {
for (j = 0; j < t; j++) {
if (!(e[j] == '_' || e[j] == '.' || isalpha(e[j]) || isdigit(e[j]))) {
p = -1;
printf("\nwrong\n");
break;
}
}
if (p == 0) {
for (i = t; i < strlen(e); i++) {
if (e[i] == '.') {
t2 = i;
countb++;
}
}
if (countb == 1) {
for (i = 0; i < t2 && i > t2; i++) {
if (!(isalpha(e[i]))) {
p = -1;
printf("\nwrong\n");
break;
} else {
p = 1;
}
}
if (p == 1) {
if (e[t2 + 3] != '\0') {
p = -1;
printf("\nwrong\n");
}
}
} else {
p =- 1;
printf("\nwrong\n");
}
}
} else {
p = -1;
printf("\nwrong\n");
}
return;
}
Instead of comparing i < strlen(e) you can test if the byte at offset i is not the null terminator: e[i] != '\0'.
You can also compute the length of the string just once at the beginning of the function and store it into a len variable.
The only thing you can do is to implement your own strlen function.
In C, strings are null-terminated char arrays, that means, the last character of an array is '\0' (zero). The strlen function iterates over the character array until a null-character is found, it returns the length of that array, the null-character not included.
To duplicate (create) a c-string, you have to do the following:
char *str = malloc(strlen(another_string) + 1);
str[strlen(another_string)] = '\0';
Example for strlen:
size_t my_strlen(const char *str)
{
if (!str) return 0;
size_t n = 0;
for (; *str; ++n, ++str);
return n;
}
You can create you own my_strlen :)
unsigned int my_strlen(char *str)
OR
you can loop while your char is different of '\0' (null byte). So just replace strlen(e) with e[i] != '\0'
It should work just fine
I want to count the number of words that are in a file. I store each line of the text in the file using a double pointer and then manipulate it do other things.
char **create2DArray()
{
int i = 0;
char **str = malloc(sizeof(char *) * 100);
for (i = 0; i < 100; i++)
{
str[i] = malloc(sizeof(char) * 1000);
}
return str;
}
char **readFile(char **str)
{
int i = 0;
FILE *pFile;
char *filename = "C:\\Users\\muham\\OneDrive\\Documents\\A2\\A2 Samples\\sample1.txt";
pFile = fopen(filename, "r");
if (pFile == NULL)
{
printf("Could not open file");
exit(1);
}
while (fgets(str[i], 1000, pFile) != NULL)
{
RemoveReturn(str[i]);
lineCount++;
printf("%s\n", str[i]);
i++;
}
fclose(pFile);
return str;
}
int wordCount(char **str)
{
int wordCounting = 0;
int i = 0;
int q = 0;
for (i = 0; i < lineCount; i++)
{
for (q = 0; q <= strlen(str[i]); q++)
{
if (*str[q] == ' ' || *str[q] == '\0')
{
wordCounting++;
}
if (*str[q] == ' ' && *str[q + 1] == ' ' && *str[0] != ' ')
{
wordCounting--;
}
if (*str[0] == ' ')
{
wordCounting--;
}
if (*str[q] == ' ' && *str[q + 1] == '\0')
{
wordCounting--;
}
if (strlen(str[q]) == 0)
{
wordCounting--;
}
}
}
printf("%d\n", wordCounting);
return wordCounting;
}
As of right now, when I run the program, wordCount prints 0. Why is this happening? Is it because I am iterating through the number of pointers with str[i] and not the strings stored in str[i]? How do I fix this?
There are several issues in your code; The most obvious one is probably your loop for (i = 0; i <= strlen(str[i]); i++), in which you compare the length of the ith string with the value of i, and you use the same i then to access the characters of the ith string. This all rarely makes sense.
I'd start with two things:
First, make sure that you do not access uninitialized rows, i.e. consider lineCount. A simple way would be to make it either a global variable or to return it in readFile; signature would change to int readFile(char **str) { ....; return lineCount; }
Second, use two nested loops:
for (int line=0; line<lineCount; line++) {
for (int column=0; column < strlen(str[line]); column++) {
// your code for detecting lines goes here...
}
}
I wrote the following function which will break in the lines marked with // Breakpoint:
char *parseNextWord(char *str)
{
static char *lastStr = "";
static int lastPosition = 0;
if (strcmp(lastStr, str) != 0)
{
lastStr = str;
lastPosition = 0;
}
if (lastPosition > 0 && str[lastPosition - 1] == 0)
{
return 0;
}
char *word = "";
int wLength = 0;
while (str[lastPosition] != ' ' && str[lastPosition] != '\n' && str[lastPosition] != '\0')
{
char *tmp = (char*)malloc(++wLength * sizeof(char));
for (int i = 0; i < sizeof(word); i++)
{
tmp[i] = word[i];
}
tmp[sizeof(*tmp) - 1] = str[lastPosition];
free(word); // Breakpoint
word = (char*)malloc(sizeof(*tmp));
for (int i = 0; i < sizeof(tmp); i++)
{
word[i] = tmp[i];
}
free(tmp); // Breakpoint
lastPosition++;
}
while (str[lastPosition - 1] != '\0' && (str[lastPosition] == ' ' || str[lastPosition] == '\n' || str[lastPosition] == '\0'))
{
lastPosition++;
}
return word;
}
The function can be called like this:
char* string = "Name1 Name2\nName3 Name4\nName1";
int totalCount = 0;
char *nextWord = parseNextWord(string);
while (nextWord != 0)
{
for (int c = 1; c < argc; c++)
{
if (strcmp((const char*)argv[c], nextWord) == 0)
{
totalCount++;
}
}
nextWord = parseNextWord(string);
}
Why is my code breaking on free? How can I improve it?
The relevant code I see is:
char* word = "";
free(word);
You did not allocate the empty string (""), so you cannot free it.
You can only free what you malloc
If you didn't allocate it, don't try to free it.
P.S. Here is my best list of functions which allocate memory that can be freed:
malloc
realloc
calloc
strdup
asprintf
vasprintf
(notably: _not_ alloca)
Maybe there are others as well?
because you didn't allocate word the first time you entered the loop.
you just make it point to "" which is not allocated dynamically.
my suggestion to make it work is to add integer variable before the while with initial value 0 :
if (flag != 0) {
free(word);
} else {
word = 1;
}
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--) {