I wrote the following function to split the given full path into directory, filename and extension.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct path_info {
char *directory;
char *filename;
char *extension;
};
#ifdef WIN32
const char directory_separator[] = "\\";
#else
const char directory_separator[] = "/";
#endif
struct path_info* splitpath(const char *full_path)
{
size_t length = strlen(full_path);
struct path_info *p = (struct path_info*) malloc(sizeof(struct path_info) + length + 3); /* Extra space for padding and shifting */
if(p)
{
char *path = (char *) &p[1]; /* copy of the path */
char *end = &path[length + 1];
char *extension;
char *last_separator;
/* copy the path */
strcpy(path, full_path);
*end = '\0';
p->directory = end;
p->extension = end;
p->filename = path;
last_separator = strrchr(path, directory_separator[0]); /* Finding the last directory separator */
if(last_separator) {
memmove(last_separator + 1, last_separator, strlen(last_separator)); /* inserting a directory separator where null terminator will be inserted */
p->directory = path;
*(++last_separator) = '\0'; /* Truncate the directory path */
p->filename = ++last_separator; /* Taking the remaining as file name */
}
/* Finding the extension starts from second character. This allows handling filenames
starts with '.' like '.emacs'.*/
extension = strrchr(&p->filename[1], '.');
if(extension) {
/* shifting the bytes to preserve the extension */
memmove(extension + 1, extension, strlen(extension)); /* problem happens here */
p->extension = extension + 1;
*extension = '\0'; /* Truncates the file name */
}
}
return p;
}
int main(void)
{
struct path_info *p = splitpath("C:\\my documents\\some.txt");
printf("Directory : %s\n", p->directory);
printf("Filename : %s\n", p->filename);
printf("Extension : %s\n", p->extension);
return 0;
}
This works well for the given input on GCC. But it fails on MSVC leaving some garbage data on extension variable. I have added a comment on the place where things go wrong. I am not understanding why memmove is behaving differently on MSVC? I have used memmove in two places and strange part is that the first one works fine.
Any help would be appreciated.
Try moving strlen(extension) + 1 bytes, so that you move not just the extension characters but the trailing null character as well. For example, if the extension was “abc”, then you're moving only 3 characters forward one space. There may have been a null character after the ‘c’ character, but no null character after that, so the string becomes unterminated when you shift the characters.
Your second memmove writes over the terminating '\0' byte. You could move strlen(extension)+1 bytes to solve that problem. I suspect that on GCC you got lucky and there happened to be an additional '\0' byte in the next memory location.
Pretty sure this has nothing to do with memmove but rather the rest of your string logic, which is a mess and very inefficient. Instead of copying at the beginning, why not just identify the 3 parts of your string and their corresponding lengths, then copy them into the destination buffer at the right offsets?
Or if you just need to use the results with printf, don't even make a copy! Just identify the lengths and do something like this:
printf("Directory: %.*s\n", dir_len, full_pathname);
printf("Filename: %.s*\n", name_len, full_pathname+name_start);
printf("Extension: %.*s\n", ext_len, full_pathname+ext_start);
The same works if you're using snprintf to format the text for display in UI elements...
You aren't including null character while memmove().
Try this:
memmove(last_separator + 1, last_separator, strlen(last_separator)+1);
memmove(extension + 1, extension, strlen(extension)+1);*/
EDIT: And here is a bit better way of the same thing that you are doing. This doesn't involve memmoves. But ofcourse you would need separate memory allocation(I am using strdup()). Nulls are also taken care of in the same memory space which was allocated by strdup().
struct path_info* splitpath1(const char *full_path)
{
char * path = strdup(full_path);
char * fileWithExt = strtok((char *)strrchr(path,'\\'),"\\");
struct path_info *p = (struct path_info*) malloc(sizeof(struct path_info)); /* Extra space for padding and shifting */
p->filename = strtok(fileWithExt, ".");
p->extension = strtok(NULL, ".");
strtok((char *)strchr(path,'\\'),"\\");
p->directory = path;
return p;
}
Related
i want to replace _ (underscore) with white spaces and make the first letter of the name and the surname to upper case while printing the nameList in searchKeyword method.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void searchKeyword(const char * nameList[], int n, const char keyword[])
{
int i,name=0;
char *str;
const char s[2] = " " ;
for(i=0;i<n;i++)
{
char *str = (char *) malloc((strlen(nameList[0])+1)*sizeof(char));
strcpy(str,nameList[i]);
strtok(str,"_");
if(strcmp(keyword,strtok(NULL,"_"))==0) // argument NULL will start string
{ // from last point of previous string
name++;
if(nameList[i] == '_')
strcpy(nameList[i],s);
//nameList[i] = ' ';
printf("%s\n",nameList[i]);
}
}
if(name==0)
{
printf("No such keyword found\n");
}
free(str); //deallocating space
}
int main()
{
char p1[] = "zoe_bale";
char p2[] = "sam_rodriguez";
char p3[] = "jack_alonso";
char p4[] = "david_studi";
char p5[] = "denzel_feldman";
char p6[] = "james_bale";
char p7[] = "james_willis";
char p8[] = "michael_james";
char p9[] = "dustin_bale";
const char * nameList[9] = {p1, p2, p3, p4, p5, p6, p7, p8, p9};
char keyword[100];
printf("Enter a keyword: ");
scanf("%s", keyword);
printf("\n");
searchKeyword(nameList, 9, keyword);
printf("\n");
for (int i = 0; i < 9; i++)
printf("%s\n",nameList[i]);
return 0;
}
Search through the strings and print the ones whose surname part is equal to keyword.
As shown in the example runs below, the strings are printed in “Name Surname” format (the first letters are capitalized).
Output should be like this:
Enter a keyword: james
Michael James
zoe_bale
sam_rodriguez
jack_alonso
david_studi
denzel_feldman
james_bale
james_willis
michael_james
dustin_bale
There is no reason to dynamically allocate storage for your name and surname. Looking at your input, neither will exceed 9-characters, so simply using an array for each of 64-chars provides 6X the storage required (if you are unsure, double that to 128-chars and have 1200% additional space). That avoids the comparatively expensive calls to malloc.
To check whether keyword exists in nameList[i], you don't need to separate the values first and then compare. Simply use strstr (nameList[i], keyword) to determine if keyword is contained in nameList[i]. If you then want to match only the name or surname you can compare again after they are separated. (up to you)
To parse the names from the nameList[i] string, all you need is a single pointer to locate the '_' character. A simple call to strchr() will do and it does not modify nameList[i] so there is no need to duplicate.
After using strchr() to locate the '_' character, simply memcpy() from the start of nameList[i] to your pointer to your name array, increment the pointer and then strcpy() from p to surname. Now you have separated name and surname, simply call toupper() on the first character of each and then output the names separate by a space, e.g.
...
#include <ctype.h>
#define NLEN 64
void searchKeyword (const char *nameList[], int n, const char keyword[])
{
for (int i = 0; i < n; i++) { /* loop over each name in list */
if (strstr (nameList[i], keyword)) { /* does name contain keyword? */
char name[NLEN], surname[NLEN]; /* storage for name, surname */
const char *p = nameList[i]; /* pointer to parse nameList[i] */
if ((p = strchr(p, '_'))) { /* find '_' in nameList[i] */
/* copy first-name to name */
memcpy (name, nameList[i], p - nameList[i]);
name[p++ - nameList[i]] = 0; /* nul-terminate first name */
*name = toupper (*name); /* convert 1st char to uppwer */
/* copy last name to surname */
strcpy (surname, p);
*surname = toupper (*surname); /* convert 1st char to upper */
printf ("%s %s\n", name, surname); /* output "Name Surname" */
}
}
}
}
Example Use/Output
Used with the remainder of your code, searching for "james" locates those names containing "james" and provides what looks like the output you requested, e.g.
$ ./bin/keyword_surname
Enter a keyword: james
James Bale
James Willis
Michael James
zoe_bale
sam_rodriguez
jack_alonso
david_studi
denzel_feldman
james_bale
james_willis
michael_james
dustin_bale
(note: to match only the name or surname add an additional strcmp before the call to printf to determine which you want to output)
Notes On Your Existing Code
Additional notes continuing from the comments on your existing code,
char *str = (char *) malloc((strlen(nameList[0])+1)*sizeof(char));
should simply be
str = malloc (strlen (nameList[i]) + 1);
You have previously declared char *str; so the declaration before your call to malloc() shadows your previous declaration. If you are using gcc/clang, you can add -Wshadow to your compile string to ensure you are warned of shadowed variables. (they can have dire consequences in other circumstances)
Next, sizeof (char) is always 1 and should be omitted from your size calculation. There is no need to cast the return of malloc() in C. See: Do I cast the result of malloc?
Your comparison if (nameList[i] == '_') is a comparison between a pointer and integer and will not work. Your compiler should be issuing a diagnostic telling you that is incorrect (do not ignore compiler warnings -- do not accept code until it compiles without warning)
Look things over and let me know if you have further questions.
that worked for me and has no memory leaks.
void searchKeyword(const char * nameList[], int n, const char keyword[])
{
int found = 0;
const char delim = '_';
for (int i = 0; i < n; i++) {
const char *fst = nameList[i];
for (const char *tmp = fst; *tmp != '\0'; tmp++) {
if (*tmp == delim) {
const char *snd = tmp + 1;
int fst_length = (snd - fst) / sizeof(char) - 1;
int snd_length = strlen(fst) - fst_length - 1;
if (strncmp(fst, keyword, fst_length) == 0 ||
strncmp(snd, keyword, snd_length) == 0) {
found = 1;
printf("%c%.*s %c%s\n",
fst[0]-32, fst_length-1, fst+1,
snd[0]-32, snd+1);
}
break;
}
}
}
if (!found)
puts("No such keyword found");
}
hopefully it's fine for you too, although I use string.h-functions very rarely.
I want to split a char array into tokens using the NUL char as the separator.
I have a char array that I've received over the network from a recv command, so I know the length of the char array. In that char array there are bunch of strings that are separated by the NUL char (\0).
Because the separator is the NUL char, that means I can't use strtok, because it uses NULL for its own purposes.
So I want to iterate through all the strings starting from byte 8 (the strings are preceded by 2 32 bit integers).
I was thinking I could iterate though all the characters looking for the \0 character and then doing a memcpy of the length I have found so far, but I figured there must be a nicer method than this.
What other approach can I take?
Here is some simple code showing how you can get the contained strings:
#include <stdio.h>
#include <string.h>
int main(void) {
char recbuf[7] = {'a', 'b', 'c', '\0', 'd', 'e', '\0'};
int recbuf_size = 7;
int j = 0;
char* p = recbuf;
while(j < recbuf_size)
{
printf("%s\n", p); // print the string found
// Here you could copy the string if needed, e.g.
// strcpy(mySavedStrings[stringCount++], p);
int t = strlen(p); // get the length of the string just printed
p += t + 1; // move to next string - add 1 to include string termination
j += t + 1; // remember how far we are
}
return 0;
}
Output:
abc
de
If you need to skip some bytes in the start of the buffer then just do:
int number_of_bytes_to_skip = 4;
int j = number_of_bytes_to_skip;
char* p = recbuf + number_of_bytes_to_skip;
Notice:
The code above assumes that the receive buffer is always correctly terminated with a '\0'. In real world code, you should check that before running the code and add error handling, e.g.:
if (recbuf[recbuf_size-1] != '\0')
{
// Some error handling...
}
NUL separation actually makes your job lot easy.
char* DestStrings[MAX_STRINGS];
int j = 0;
int length = 0;
inr prevLength =0;
int offset = 8;
for(int i = 0;i<MAX_STRINGS;i++)
{
length += strlen(&srcbuffer[j+offset+length]);
if(length == prevLength)
{
break;
}
else
{
DestStrings[i] = malloc(length-prevLength+1);
strcpy(DestStrings[i],&srcbuffer[j+offset+length]);
prevLength = length;
j++;
}
}
You need add few additional checks to avoid potential buffer overflow errors.
Hope this code gives you slight idea about how to go ahead.
EDIT 1:
Though this is the code to start with an not the entire solution due to down-votes modifying the index.
EDIT 2 :
As the length of received data buffer is already known ,please append NUL to received data to make this code work as it is. On the other hand length of received data can itself used to compare with copied length.
I'd suggest using a structure implementing the tokenizer for doing such kind of work. It'll be easier to read and to maintain because it looks similar to object oriented code. It isolates the memcpy, so I think it's "nicer".
First, the headers I'll use:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
The Tokenizer structurehas to remember the beginning of the string (so that we can erase the memory after it's not needed anymore), the actual index and the end index to check if we already parsed the whole string:
struct Tokenizer {
char *string;
char *actual_index;
char *end_index;
};
I suggest using a factory-like function to create a tokenizer. It's constructed here, copying the input string using memcpy because string.h functions stop on the first '\0' character.
struct Tokenizer getTokenizer(char string[], unsigned length) {
struct Tokenizer tokenizer;
tokenizer.string = (char *)malloc(length);
tokenizer.actual_index = tokenizer.string;
tokenizer.end_index = tokenizer.string + length;
memcpy(tokenizer.string, string, length);
return tokenizer;
}
Now the function responsible for getting the tokens. It returns new allocated strings, which have a '\0' character at their end. It also changes the address actual_index is pointing to. It takes the address of the tokenizer as its argument, so it can change its values:
char * getNextToken(struct Tokenizer *tokenizer) {
char * token;
unsigned length;
if(tokenizer->actual_index == tokenizer->end_index)
return NULL;
length = strlen(tokenizer->actual_index);
token = (char *)malloc(length + 1);
// + 1 because the '\0' character has to fit in
strncpy(token, tokenizer->actual_index, length + 1);
for(;*tokenizer->actual_index != '\0'; tokenizer->actual_index++)
; // getting the next position
tokenizer->actual_index++;
return token;
}
Sample use of the tokenizer, to show how to handle the memory allocation ang how to use it.
int main() {
char c[] = "Lorem\0ipsum dolor sit amet,\0consectetur"
" adipiscing elit. Ut\0rhoncus volutpat viverra.";
char *temp;
struct Tokenizer tokenizer = getTokenizer(c, sizeof(c));
while((temp = getNextToken(&tokenizer))) {
puts(temp);
free(temp);
}
free(tokenizer.string);
return 0;
}
Assuming this input data:
char input[] = {
0x01, 0x02, 0x0a, 0x0b, /* A 32bit integer */
'h', 'e', 'l', 'l', 'o', 0x00,
'w', 'o', 'r', 'l', 'd', 0x00,
0x00 /* Necessary to make the end of the payload. */
};
A 32 integer in the beginning gives:
const size_t header_size = sizeof (uint32_t);
Parsing the input can be done by identifying the "string"'s 1st character and storing a pointer to it and then moving on exactly as much as the string found is long (1+) then start over until the end of input had been reached.
size_t strings_elements = 1; /* Set this to which ever start size you like. */
size_t delta = 1; /* 1 is conservative and slow for larger input,
increase as needed. */
/* Result as array of pointers to "string": */
char ** strings = malloc(strings_elements * sizeof *strings);
{
char * pc = input + header_size;
size_t strings_found = 0;
/* Parse input, if necessary increase result array, and populate its elements: */
while ('\0' != *pc)
{
if (strings_found >= strings_elements)
{
strings_elements += delta;
void * pvtmp = realloc(
strings,
(strings_elements + 1) * sizeof *strings /* Allocate one more to have a
stopper, being set to NULL as a sentinel.*/
);
if (NULL == pvtmp)
{
perror("realloc() failed");
exit(EXIT_FAILURE);
}
strings = pvtmp;
}
strings[strings_found] = pc;
++strings_found;
pc += strlen(pc) + 1;
}
strings[strings_found] = NULL; /* Set a stopper element.
NULL terminate the pointer array. */
}
/* Print result: */
{
char ** ppc = strings;
for(; NULL != *ppc; ++ppc)
{
printf("%zu: '%s'\n", ppc - strings + 1, *ppc)
}
}
/* Clean up: */
free(strings);
If you need to copy on split, replace this line
strings[strings_found] = pc;
by
strings[strings_found] = strdup(pc);
and add clean-up code after using and before free()ing strings:
{
char ** ppc = strings;
for(; NULL != *ppc; ++ppc)
{
free(*ppc);
}
}
The code above assume that at least 1 '\0' (NUL aka null-character) follows the payload.
If the latter condition is not met you need to either have any other terminating sequence be defined/around or need know the size of the input from some other source. If you don't your issue is not solvable.
The code above needs the following headers:
#include <inttypes.h> /* for int32_t */
#include <stdio.h> /* for printf(), perror() */
#include <string.h> /* for strlen() */
#include <stdlib.h> /* for realloc(), free(), exit() */
as well as it might need one of the following defines:
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
or what else your C compiler requires to make strdup() available.
So right now I'm trying to splice in a directory name into the middle of a pathname.
For example, say I want to splice in DIRNAME right after where OTHERDIRNAME occurs in the path. For example, let's say the path is:
/home/user/folder/OTHERDIRNAME/morefolders/test/etc
My goal is to get a new pathname that looks like this:
/home/user/folder/OTHERDIRNAME/DIRNAME/morefolders/test/etc
By the way, I have the variables for holding the old pathname and the name of the directory where I want the new directory spliced into. So I just need help using str functions in C to try and actually splice DIRNAME in at the right location. I've tried using strtok, but I seem to run into issues using OTHERDIRNAME as the delimeter, as I think that the delimeter argument needs to be a single character...
#include <stdio.h>
#include <string.h>
int main()
{
char str[128] = "/home/user/folder/OTHERDIRNAME/morefolders/test/etc";
char* delim = "/";
char* tok;
char buf[128];
tok = strtok(str,delim);
strcpy(buf,"/");
while(tok)
{
strcat(buf,tok);
strcat(buf,"/");
if(strcmp(tok,"OTHERDIRNAME") == 0)
{
strcat(buf,"DIRNAME");
strcat(buf,"/");
}
tok = strtok(NULL,delim);
}
printf("Dir path: %s\n", buf);
}
Output
Dir path: /home/user/folder/OTHERDIRNAME/DIRNAME/morefolders/test/etc/
It's pretty straightforward, although potentially confusing. Use strstr to search the source string for your "delimiter". Add the length of the delimiter so we point to the position of the splice. And then 3 memcpy calls of the appropriate lengths.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
char *dir = "/home/user/folder/OTHERDIRNAME/morefolders/test/etc";
char *seek = "/OTHERDIRNAME/";
char *ins = "DIRNAME/";
char *splice_point;
char *result;
splice_point = strstr(dir, seek); // points to the first slash we're interested in
splice_point += strlen(seek); // now points to the second slash
result = malloc(strlen(dir)+strlen(ins)+1); // allocate string of appropriate length
memcpy(result, dir, splice_point - dir); // copy the head
memcpy(result + (splice_point - dir), ins, strlen(ins)); // copy the splice
strcpy(result + (splice_point - dir) + strlen(ins), splice_point); // copy the tail (and term)
printf("%s\n", result);
}
You're right, strtok will match any single character in the second parameter as a delimiter.
strstr() should do what you want.
Off the top of my head (uncompiled and untested):
// Returns a malloc'd null-terminated string or NULL
char * replaceInString(const char * original, const char * match, const char * replace)
{
const char * foundMatch = strstr(original, match);
if (foundMatch)
{
ptrdiff_t offset = foundMatch - original;
int matchLength = strlen(match);
int replaceLength = strlen(replace);
char * newString = malloc(strlen(original) + replaceLength - matchLength + 1);
strncpy(newString, original, offset);
strcpy(newString + offset, replace);
strcpy(newString + offset + replaceLength, original + offset + matchLength);
return newString;
}
return NULL;
}
// snip
char * newDirName = replaceInString(oldDirName, "/OTHERDIRNAME/", "/OTHERDIRNAME/DIRNAME/");
// snip
Of course, adapt memory management to your needs. You could do the operation in-place if the buffer is guaranteed to be big enough.
I'm trying to dynamically obtain the parent directory (let's say C:\some\dir) from a file name I get in an argument (say C:\some\dir\file), and put it in a char*. I already have the full path and file in a char*. How exactly would I do that in C?
I have some code but in my mind it's all garbled and I can't make any sense of it. How should I rework/rewrite this?
/* Gets parent directory of file being compiled */
short SlashesAmount;
short NamePosition;
short NameLength;
char* Pieces[SlashesAmount];
char* SplitPath;
short ByteNumber;
short PieceNumber;
char* ScriptDir;
NameLength = strlen(File);
//Dirty work
SplitPath = strtok(File, "\");
do {
ByteNumber = 0;
do {
File[NamePosition] = CurrentPiece[ByteNumber];
NamePosition++;
} while(File[NamePosition] != '\n');
PieceNumber++;
} while(NamePosition < NameLength);
What you're looking for is dirname(3). This is POSIX-only.
A Windows alternative would be _splitpath_s.
errno_t _splitpath_s(
const char * path,
char * drive,
size_t driveNumberOfElements,
char * dir,
size_t dirNumberOfElements,
char * fname,
size_t nameNumberOfElements,
char * ext,
size_t extNumberOfElements
);
Sample code (untested):
#include <stdlib.h>
const char* path = "C:\\some\\dir\\file";
char dir[256];
_splitpath_s(path,
NULL, 0, // Don't need drive
dir, sizeof(dir), // Just the directory
NULL, 0, // Don't need filename
NULL, 0);
You already have the full path of the file (for example: C:\some\dir\file.txt), just:
1. find the last slash by strrchr() : called p
2. copy from the beginning of the path to the p - 1 (do not include '/')
So the code will look like:
char *lastSlash = NULL;
char *parent = NULL;
lastSlash = strrchr(File, '\\'); // you need escape character
parent = strndup(File, strlen(File) - (lastSlash - 1));
int len = strlen(filepath);
char* dir = malloc(len + 1);
strcpy(dir, filepath);
while (len > 0) {
len--;
if (dir[len] == '\\' || dir[len] == '/') {
dir[len] = '\0';
break;
}
}
Don't have enough reputation to comment so add a answer here.
As Grijesh Chauhan commented in question, you can use strrchr() to get a shorter version of your original string.
However, the return value of strrchr() is char *, which SHOULD NOT be assigned to \0 that makes it points to nothing, instead, you can use * or [0] to modify it's first element to shorten the original string.
LIKE THIS:
strrchr(File, '\\')[0] = '\0'
// or this
*(strrchr(File, '\\') = '\0'
Great answer though, Grijesh should make it a answer :D
Here is a function that gets the full path and a buffer.
it will update the buffer to the parent path.
The function is checked. enjoy :)
/*
Use: Get parent path by full path
Input: full path, parent buffer
Output: None
*/
void getParent(char* path, char* parent)
{
int parentLen;
char* last = strrchr(path, '/');
if (last != NULL) {
parentLen = strlen(path) - strlen(last + 1);
strncpy(parent, path, parentLen);
}
}
I have to use fputs to print something and fputs take "const char *str" to print out.
I have 3 strings to print(I don't care if it's strings or char[]) as str.
I dont know the right way to do it. I used 3 string and I added them to one but is not working. I also tried to convert string to char but nothing is working!
Any recommendations?
struct passwd* user_info = getpwuid(getuid());
struct utsname uts;
uname(&uts);
I want my char const *str = user_info->pw_name + '#' + uts.nodename
You need to create a new string for that. I have no idea why you need the fputs restriction, but I assume that even if you can't/don't want to use fprintf, you still have snprintf available. You'd then do it like this:
char *new_str;
int new_length;
// Determine how much space we'll need.
new_length = snprintf(NULL, "%s#%s", user_info->pw_name, uts.nodename);
if (new_length < 0) {
// Handle error here.
}
// Need to allocate one more character for the NULL termination.
new_str = malloc(new_length + 1);
// Write new string.
snprintf(new_str, "%s#%s", user_info->pw_name, uts.nodename);
A possible solution:
/* 1 for '#' and 1 for terminating NULL */
int size = strlen(user_info->pw_name) + strlen(uts.nodename) + 2;
char* s = malloc(size);
strcpy(s, user_info->pw_name);
strcat(s, "#");
strcat(s, uts.nodename);
/* Free when done. */
free(s);
EDIT:
If C++ you can use std::string:
std::string s(user_info->pw_name);
s += "#";
s += uts.nodename;
// s.c_str(); this will return const char* to the string.