Delimiting file path in C using strtok() - c

In my current code, I am trying to pass in a file path, separate the file path by "/" , and then checking through each delimited value against every element in an array named categories using a for loop. If any element in the array matches as a substring using strstr(), it will return the value of the element, if not it will return "Others".
I managed to produce the output that I want, however I realise that whenever there is an even number of elements in categories array, the output will be 1 of the value from the file path such as
If the file path is
"/a/b/Lib/Contact/c"
If the categories are
char *categories[] = {"Library","Applications","Contact","dsdfd"};
the output will be b.
Here is my code for your reference:
const char *categorize(char*path)
{
int i = 0;
char str[1024];
char *token;
char *delim = "/";
char *categories[] = {"Library","Applications","Contact","dsdfd"};
bool check = false;
strcpy(str,path);
token = strtok(str,delim);
while (token !=NULL)
{
if(check == true)
break;
//printf("%s\n",token);
for (i = 0; i <(sizeof(categories)/sizeof(char*)); i++)
{
if(categories[i] == NULL)
break;
if(strstr(token, categories[i]))
{
check = true;
break;
}
}
token = strtok (NULL, delim);
}
if (check == true)
return "Others";
else
return categories[i];
}
Edit: I have encountered another problem. I tried using a test case if the file path does not contain any substring. It should return "Others", however it is returning me "/".
Edit2: I have sort of solved the problem by changing the if statement to check == true. However, I realise that my code here is not good which #LưuVĩnhPhúc and #fasked mentioned in the comments and I would hope to possibly fixed it.

Change your code to
for (i = 0; i <(sizeof(categories)/sizeof(char*)); i++)
sizeof(categories) will give your 16 bytes (size of 4 char*) you will have to devide that with each char* size to get number of elements in categories;

Related

Multiple strings in 2d array assigning in C

I am facing a problem while I am trying to assign multiple strings in a 2d array in C
While I am reading a file as you can see with name log, if i see the userid which is a string " " I keep the line in array users as a string with method strcpy() because this worked.Then if a condition happens I want this string to pass it to another array with name denied_users but I get the error here.I also tried strcpy(denied_users[find_result-1],users[userslen]) but also didn't work.
char string[512], *access_denied, *userid;
access_denied = "Action denied flag: 1"; //key word that lookin for
userid = "User ID: ";
int line_num = 1;
int find_result = 0;
char users[1000][1000];
char denied_users[1000][1000];
int userslen = 0;
while (fgets(string, 512, log) != NULL) {
if ((strstr(string, userid)) != NULL)
strcpy(users[userslen], string);
userslen++;
if ((strstr(string, access_denied)) != NULL) {
denied_users[find_result - 1] = users[userslen]; //how to //pass it here
find_result++;
}
line_num++;
}

I have a 'Segmentation Problem' while printing parsed parts of a String

I am writing a simple Shell for school assignment and stuck with a segmentation problem. Initially, my shell parses the user input to remove whitespaces and endofline character, and seperate the words inside the input line to store them in a char **args array. I can seperate the words and can print them without any problem, but when storing the words into a char **args array, and if argument number is greater than 1 and is odd, I get a segmentation error.
I know the problem is absurd, but I stuck with it. Please help me.
This is my parser code and the problem occurs in it:
char **parseInput(char *input){
int idx = 0;
char **parsed = NULL;
int parsed_idx = 0;
while(input[idx]){
if(input[idx] == '\n'){
break;
}
else if(input[idx] == ' '){
idx++;
}
else{
char *word = (char*) malloc(sizeof(char*));
int widx = 0; // Word index
word[widx] = input[idx];
idx++;
widx++;
while(input[idx] && input[idx] != '\n' && input[idx] != ' '){
word = (char*)realloc(word, (widx+1)*sizeof(char*));
word[widx] = input[idx];
idx++;
widx++;
}
word = (char*)realloc(word, (widx+1)*sizeof(char*));
word[widx] = '\0';
printf("Word[%d] --> %s\n", parsed_idx, word);
if(parsed == NULL){
parsed = (char**) malloc(sizeof(char**));
parsed[parsed_idx] = word;
parsed_idx++;
}else{
parsed = (char**) realloc(parsed, (parsed_idx+1)*sizeof(char**));
parsed[parsed_idx] = word;
parsed_idx++;
}
}
}
int i = 0;
while(parsed[i] != NULL){
printf("Parsed[%d] --> %s\n", i, parsed[i]);
i++;
}
return parsed;
}
In your code you have the loop
while(parsed[i] != NULL) { ... }
The problem is that the code never sets any elements of parsed to be a NULL pointer.
That means the loop will go out of bounds, and you will have undefined behavior.
You need to explicitly set the last element of parsed to be a NULL pointer after you parsed the input:
while(input[idx]){
// ...
}
parsed[parsed_idx] = NULL;
On another couple of notes:
Don't assign back to the same pointer you pass to realloc. If realloc fails it will return a NULL pointer, but not free the old memory. If you assign back to the pointer you will loose it and have a memory leak. You also need to be able to handle this case where realloc fails.
A loop like
int i = 0;
while (parsed[i] != NULL)
{
// ...
i++;
}
is almost exactly the same as
for (int i = 0; parsed[i] != NULL; i++)
{
// ...
}
Please use a for loop instead, it's usually easier to read and follow. Also for a for loop the "index" variable (i in your code) will be in a separate scope, and not available outside of the loop. Tighter scope for variables leads to less possible problems.
In C you shouldn't really cast the result of malloc (or realloc) (or really any function returning void *). If you forget to #include <stdlib.h> it could lead to hard to diagnose problems.
Also, a beginner might find the -pedantic switch helpful on your call to the compiler. That switch would have pointed up most of the other suggestions made here. I personally am also a fan of -Wall, though many find it annoying instead of helpful.

Using malloc with functions and strcmp

I'm trying to use malloc with a function I wrote in order to grow a list of unique states(no duplicates)
My file contains strings such as;
Kmart, 295 Hartford Turnpike, Vernon CT
The function I wrote extracts the states("CT") from a file;
#define MAX_CHARS_PER_LINE 80
void getState(char strState[], const char strLine[])
{
char newLine[MAX_CHARS_PER_LINE+1];
char newState[MAX_CHARS_PER_LINE+1];
strcpy(newLine, strLine);
char* token = strtok(newLine, ",");
if(token != NULL)
{
token = strtok(NULL,",");
token = strtok(NULL, ",");
}
strcpy(newState, token);
unsigned long length = strlen(newState)-5;
strcpy(strState, newState+length);
}
This is my main function which I am trying to find the unique list of states using strcmp and grow it using malloc;
int main(void)
{
char **states[3];
char buffer[MAX_CHARS_PER_LINE+1];
FILE* fptr;
fptr = fopen(fileName, "r");
if(fptr == NULL)
{
exit(EXIT_FAILURE);
}
else
{
while(fgets(buffer, sizeof(buffer), fptr))
{
getState(states, buffer);
}
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 3; j++)
{
if(strcmp(states[i], states[j]))
{
states[i] = malloc(3* sizeof(states));
}
}
}
fclose(fptr);
free(states);
}
return EXIT_SUCCESS;
}
I'm a bit confused on how to correctly use malloc and strcmp to get this unique list. My get state function works fine, it's just my main I have problems with
A few things:
a. In the getState function, change the length variable from strlen(newState)-5 to strlen(newState)-2. States are only 2 letters, and strlen doesn't count the terminating character.
b. Don't use a triple pointer for "states"; use a double pointer. It should just be a string of strings.
c. Use the iterator for the states list for getState. MAKE SURE TO RE-MALLOC TO INCREASE THE SIZE AND COPY THE OLD STATES WITH A BACKUP POINTER BEFORE CALLING GETSTATES
d. Iterate through the states array with getState.
e. Make either a second array for the second iteration to copy each unique state name, OR just make a new string variable, use getState on that, then iterate through states with strcmp, and if there's no matches, THEN add that state to the states array.

Blank Line Seg. Fault (Writing Own Shell)

I'm still learning about writing simple shell.
I want this shell to allow blank lines and comments.
I did some coding and I encountered a problem that if I directly just input enter (blank line), it directly seg.fault core dumped.
I don't know exactly where's the mistake, because I print everything and all seems fine. The only thing that I suspicious in these line
if (args[0] == NULL || !(strncmp(args[0],"#",1))) {
exitstat = 0;
}
I got the args from basic split command function. The weird thing is the comments works just fine.
Below is my functions for read what user input and split them (tokenize if I'm not mistaken). They are really basic because I'm learn those functions from internet tutorial.
char *commandInput() {
char *command = NULL;
ssize_t bufsize = 0;
getline(&command, &bufsize, stdin);
return command;
}
char **splitLine(char *command) {
int bufsize = 64,
int position = 0;
char **tokens = malloc(bufsize * sizeof(char*));
char *token;
token = strtok(command, DELIMITER);
while (token != NULL) {
tokens[position] = token;
position++;
if (position >= bufsize) {
bufsize += 64;
tokens = realloc(tokens, bufsize * sizeof(char*));
}
token = strtok(NULL, DELIMITER);
}
tokens[position] = NULL;
return tokens;
}
Anybody could help me recognize what makes it seg.fault if I enter blank line? Thank you.
EDIT
I used debugger (finally succeed to use it after several trial) and it turns out that the error is located at the line that I didn't expect to cause any problem (see ---UPDATE----).
They way I handle my commandInput function is in main() function, I write
int main () {
......
char * command = NULL
char **args;
command = commandInput();
args= splitLine(command);
------------------ UPDATE!(CAUSING ERROR IF STATEMENT) ---------------
background = 0
numbarguments = 0
// Condition to check whether there is a start program running in backgrond
if (!(strncmp(args[numbarguments - 1], "&",1))) {
background = 1;
args[numbarguments - 1] = NULL;
}
----------------------------------------------
if (args[0] == NULL || !(strncmp(args[0],"#",1))) {
exitstat = 0;
}
....... //(comparing the arguments other than null)
}
So any advice regarding that if condition that causing me seg.fault. Thank you.
The parameter you pass to splitline is modified. strtok has the effect of modifying the string it gets by inserting \0's and returning a pointer to substrings. What strtok returns is not something you can directly store for later use, instead you need to make a copy of it.
token = strtok(command, DELIMITER);
while (token != NULL)
{
tokens[position] = malloc(strlen(token)+1);
strcpy(tokesn[position],token);
...
so in other words it is not enough to allocate the array of pointers to strings, you also need to allocate space to hold strings that you tokenize with strtok.
The code
if (!(strncmp(args[numbarguments - 1], "&",1))) {
background = 1;
args[numbarguments - 1] = NULL;
}
looks wrong, numberarguments is initially 0 so you are comparing args[-1] with "&" then later you assign args[-1] = NULL which probably causes the seg fault.

Why is my program printing out more times than expected?

I have a program that reads in a file, goes through line by line, and breaks up the line word by word. My problem is, I am about to store each word in a array but I need to use strcmp function to verify the word doesnt already exist. Anyways below is my code and my question is, why is my program printing out 1 so many times? I was expecting it to only print it out twice because this occurs twice in my text file.
while (fgets(line, sizeof(line), fi) != NULL) { // looping through each line
line_count += 1;
for (j = 0; j < sizeof(line); j++) { // convert all words to lowercase
line[j] = tolower(line[j]);
}
result = strtok(line, delimiters);
while (result != NULL) {
word_count += 1;
if (strcmp(result, "this")) {
printf("1\n");
}
result = strtok(NULL, delimiters); // get the next token
}
}
Below is my text file:
This is the first test.
This is the second test.
strcmp() returns 0 if the string matches. You're checking for a truthy value. You really want strcmp(result, "this") == 0.
You will also need to make the match case insensitive, which is usually called stricmp().
Do you try again after change "strcmp(result, "this")" to "strcmp(result, "This")" ?

Resources