I'm trying to obtain a file from its path.
This is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char split_path(char *path){
char *str1, *delim;
char buf[255];
int i, max;
char *token = "/";
for (i = 1, str1 = path; ; i++, str1 = NULL){
delim = strtok(str1, token);
if (delim == NULL){
break;
}
buf[i] = *delim;
max = i;
}
char last = buf[max];
return last;
}
int main(int argc, char *argv[]){
if (argc != 3){
fprintf(stderr, "Usage: %s string delim\n", argv[0]);
exit(EXIT_FAILURE);
}
char last = split_path(argv[1]);
printf("%c\n", last);
return 0;
}
So if I enter an input of /a/b/c/d , it returns 'd' which is what I want.
But if I enter an input of /a/b/c/d.txt, it only returns 'd' without the '.txt', when what I need is 'd.txt'.
It seems to be only getting the first index after the last / . I can't seem to figure out the problem.
Help much appreciated.
Your problem is split_path is returning the first letter of the last good result of strtok.
Change split_path to return a string, and modify it slightly... something like this:
char *split_path(char *path){
char *str1, *delim;
int i, max;
char *last = NULL;
char *token = "/";
for (i = 1, str1 = path; ; i++, str1 = NULL){
delim = strtok(str1, token);
if (delim == NULL){
break;
}
last = delim;
}
return last;
}
int main(int argc, char *argv[]){
if (argc != 3){
fprintf(stderr, "Usage: %s string delim\n", argv[0]);
exit(EXIT_FAILURE);
}
char *last = split_path(argv[1]);
printf("%s\n", last);
return 0;
}
I'll show you how to do it two ways. The easiest is to include libgen.h and use basename(), if this exists on your system. It does what you wanted. But if you want to write your own function, you can do it with strrchr(), which finds the last occurrence of a character in a string.
#include <stdio.h>
#include <string.h> // for strrchr()
#include <libgen.h> // for basename()
const char *my_basename(const char *path)
{
const char *lastSlash = strrchr(path, '/');
if (lastSlash)
return lastSlash+1;
return path;
}
int main(int argc, char *argv[])
{
if (argc < 2)
return;
printf("Full path = %s\n", argv[1]);
printf("basename() = %s\n", basename(argv[1]));
printf("my_basename() = %s\n", my_basename(argv[1]));
return 0;
}
Sample output:
Full path = /aaa/bbb/ccc/ddd.txt
Filename = ddd.txt
Filename = ddd.txt
Related
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *ptr;
char temp[20];
if (strlen(argv[1]) < strlen(argv[2]))
{
strcpy(temp,argv[1]);
strcpy(argv[1],argv[2]);
strcpy(argv[2],temp);
}
ptr = strstr(argv[1],argv[2]);
if (ptr == NULL)
printf("Non-inclusive");
else
printf("%s is part of %s", argv[2], argv[1]);
return 0;
}
When I enter "abc abcd",
I want to get "abc is part of abcd" as a result,
but real result is "abc is part of abcdabc"
The length of each string in the argv array is fixed. So when you attempt to swap the contents of argv[1] and argv[2] when their sizes are different you write past the end of the shorter one. This triggers undefined behavior.
Better to use separate char * variables, one pointing the longer string and one pointer to the shorter.
int main(int argc, char *argv[])
{
char *ptr;
char *s_short, *s_long;
if (strlen(argv[1]) < strlen(argv[2])) {
s_short = argv[1];
s_long = argv[2];
} else {
s_short = argv[2];
s_long = argv[1];
}
ptr = strstr(s_long,s_short);
if (ptr == NULL)
printf("Non-inclusive");
else
printf("%s is part of %s", s_short, s_long);
return 0;
}
In C programming language, is it possible to access int argc or char **argv without using the parameters? I know some of you might ask why this is needed, just for research purposes.
Is it possible to generate the cmd line arguments without using the main parameter variables ? For example, to illustrate some pseudo code, that i have in mind,
LPTSTR cmd = GetCommandLine();
splitted = cmd.split(" ") //split from spaces
char **someArgv.pushForEach Splitted, length++
and you'd have a someArgv with the parameters and length as argc, this'd really help to know if possible to illustrate.
If OP already has the command as a string, then:
Form a copy of the string
Parse it for argument count
Allocate for argv[]
Parse & tokenize copy for each argv[]
Call main()
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
// Not standard, but commonly available
char *strdup(const char *s);
// Return length of token and adjust offset to the next one
// Adjust as needed
// Presently only ' ' are used to separate
// More advanced would have escape characters, other white-space, etc.
size_t tokenize(const char *s, size_t *offset) {
// find following space
size_t len = strcspn(s + *offset, " ");
*offset += len;
// find first non-space
*offset += strspn(s + *offset, " ");
return len;
}
int call_main(const char *cmd) {
char *cmd2 = strdup(cmd);
cmd2 += strspn(cmd2, " "); // skip leading spaces
size_t offset = 0;
int argc = 0;
while (tokenize(cmd2, &offset) > 0) {
argc++;
}
char **argv = malloc(sizeof *argv * ((unsigned)argc + 1u));
offset = 0;
for (int a = 0; a < argc; a++) {
argv[a] = &cmd2[offset];
size_t len = tokenize(cmd2, &offset);
argv[a][len] = '\0';
}
argv[argc] = NULL;
int retval = 0;
#if 0
retval = main(argc, argv);
#else
printf("argc:%d argv:", argc);
for (int a = 0; a < argc; a++) {
printf("%p \"%s\", ", argv[a], argv[a]);
}
printf("%p\n", argv[argc]);
#endif
free(cmd2);
free(argv);
return retval;
}
Sample
int main() {
call_main(" name 123 abc 456 ");
}
argc:4 argv:0x800062322 "name", 0x800062327 "123", 0x80006232c "abc", 0x800062331 "456", 0x0
Pedantic: The strings provided to main() should be modifiable. Avoid code like
argv[1] = "Hello";
....
main(argc, argv);
#include <stdio.h>
int main(int argc, char *argv[]);
int callMain(void)
{
char *argv[4];
argv[0] = "binary";
argv[1] = "param1";
argv[2] = "param2";
argv[3] = NULL;
return main(3, argv);
}
int main(int argc, char *argv[])
{
if (argc <= 1)
{
return callMain();
}
printf("ARGC: %u\n", argc);
int i;
for (i = 0; i < argc; i++)
printf("ARG: %u - %s\n", i, argv[i]);
return 0;
}
I wrote the below code which replaces '|' characters from the string.
#include <stdio.h>
#include <stdlib.h>
void remove_pipes(char*);
main (int argc, char **argv)
{
char string1[] = "|||||||||||||";
remove_pipes(string1);
printf("String1 = %s", string1);
char string2[] = "h|e|l|l|o";
remove_pipes(string2);
printf("String2 = %s", string2);
}
void remove_pipes(char* input)
{
for(; *input; input++)
{
if(*input == '|')
{
*input = ' ';
}
}
}
Now I need to modify this method to remove the '|' character from the string. I am not sure how to do that. Hope someone can give me some hint.
Use a char pointer to travel the input and modify it:
#include <stdio.h>
#include <stdlib.h>
void remove_pipes(char*);
main (int argc, char **argv)
{
char string1[] = "|||||||||||||";
printf("String1 = %s\n", string1);
remove_pipes(string1);
printf("String1 = %s\n", string1);
char string2[] = "h|e|l|l|o";
printf("String2 = %s\n", string2);
remove_pipes(string2);
printf("String2 = %s\n", string2);
}
void remove_pipes(char* input)
{
unsigned idx = 0;
char* aux = input;
for(; *input; input++)
{
if (*input != '|')
{
*(aux + idx++) = *input;
}
}
*(aux + idx) = '\0';
}
I need to use strtok to read in a first and last name and seperate it. How can I store the names where I can use them idependently in two seperate char arrays?
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] ="test string.";
char * test;
test = strtok (str," ");
while (test != NULL)
{
printf ("%s\n",test);
test= strtok (NULL, " ");
}
return 0;
}
Here is my take at a reasonably simple tokenize helper that
stores results in a dynamically growing array
null-terminating the array
keeps the input string safe (strtok modifies the input string, which is undefined behaviour on a literal char[], at least I think in C99)
To make the code re-entrant, use the non-standard strtok_r
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char** tokenize(const char* input)
{
char* str = strdup(input);
int count = 0;
int capacity = 10;
char** result = malloc(capacity*sizeof(*result));
char* tok=strtok(str," ");
while(1)
{
if (count >= capacity)
result = realloc(result, (capacity*=2)*sizeof(*result));
result[count++] = tok? strdup(tok) : tok;
if (!tok) break;
tok=strtok(NULL," ");
}
free(str);
return result;
}
int main ()
{
char** tokens = tokenize("test string.");
char** it;
for(it=tokens; it && *it; ++it)
{
printf("%s\n", *it);
free(*it);
}
free(tokens);
return 0;
}
Here is a strtok-free reimplementation of that (uses strpbrk instead):
char** tokenize(const char* str)
{
int count = 0;
int capacity = 10;
char** result = malloc(capacity*sizeof(*result));
const char* e=str;
if (e) do
{
const char* s=e;
e=strpbrk(s," ");
if (count >= capacity)
result = realloc(result, (capacity*=2)*sizeof(*result));
result[count++] = e? strndup(s, e-s) : strdup(s);
} while (e && *(++e));
if (count >= capacity)
result = realloc(result, (capacity+=1)*sizeof(*result));
result[count++] = 0;
return result;
}
Do you need to store them separately? Two pointers into a modified char array will yield two separate perfectly usable strings.
That is we transform this:
char str[] ="test string.";
Into this:
char str[] ="test\0string.";
^ ^
| |
char *s1 ----- |
char *s2 -----------
.
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] ="test string.";
char *firstname = strtok(str, " ");
char *lastname = strtok(NULL, " ");
if (!lastname)
lastname = "";
printf("%s, %s\n", lastname, firstname);
return 0;
}
What about using strcpy:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAX_NAMES 2
int main ()
{
char str[] ="test string.";
char *names[MAX_NAMES] = { 0 };
char *test;
int i = 0;
test = strtok (str," ");
while (test != NULL && i < MAX_NAMES)
{
names[i] = malloc(strlen(test)+1);
strcpy(names[i++], test);
test = strtok (NULL, " ");
}
for(i=0; i<MAX_NAMES; ++i)
{
if(names[i])
{
puts(names[i]);
free(names[i]);
names[i] = 0;
}
}
return 0;
}
It contains much clutter to maintain a complete program and clean its resources, but the main point is to use strcpy to copy each token into its own string.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char** split(const char *str, const char *delimiter, size_t *len){
char *text, *p, *first, **array;
int c;
char** ret;
*len = 0;
text=strdup(str);
if(text==NULL) return NULL;
for(c=0,p=text;NULL!=(p=strtok(p, delimiter));p=NULL, c++)//count item
if(c==0) first=p; //first token top
ret=(char**)malloc(sizeof(char*)*c+1);//+1 for NULL
if(ret==NULL){
free(text);
return NULL;
}
strcpy(text, str+(first-text));//skip until top token
array=ret;
for(p=text;NULL!=(p=strtok(p, delimiter));p=NULL){
*array++=p;
}
*array=NULL;
*len=c;
return ret;
}
void free4split(char** sa){
char **array=sa;
if(sa!=NULL){
free(array[0]);//for text
free(sa); //for array
}
}
int main(void){
char str[] ="test string.";
char **words;
size_t len=0;
int i;
words = split(str, " \t\r\n,.", &len);
/*
for(char **wk = words; *wk ;wk++){
printf("%s\n", *wk);
}
*/
for(i = 0;i<len;++i){
printf("%s\n", words[i]);
}
free4split(words);
return 0;
}
/* result:
test
string
*/
Copy the results from strtok to a new buffer using a function such as
/*
* Returns a copy of s in freshly allocated memory.
* Exits the process if memory allocation fails.
*/
char *xstrdup(char const *s)
{
char *p = malloc(strlen(s) + 1);
if (p == NULL) {
perror("memory allocation failed");
exit(1);
}
strcpy(p, s);
return p;
}
Don't forget to free the return values when you're done with them.
IMO, you don't need (and probably don't want) to use strtok at all (as in, "for this, or much of anything else"). I think I'd use code something like this:
#include <string.h>
#include <stdlib.h>
static char *make_str(char const *begin, char const *end) {
size_t len = end-begin;
char *ret = malloc(len+1);
if (ret != NULL) {
memcpy(ret, begin, len);
ret[len]='\0';
}
return ret;
}
size_t tokenize(char *tokens[], size_t max, char const *input, char const *delims) {
int i;
char const *start=input, *end=start;
for (i=0; *start && i<max; i++) {
for ( ;NULL!=strchr(delims, *start); ++start)
;
for (end=start; *end && NULL==strchr(delims, *end); ++end)
;
tokens[i] = make_str(start, end);
start = end+1;
}
return i;
}
#ifdef TEST
#define MAX_TOKENS 10
int main() {
char *tokens[MAX_TOKENS];
int i;
size_t num = tokenize(tokens, MAX_TOKENS, "This is a longer input string ", " ");
for (i=0; i<num; i++) {
printf("|%s|\n", tokens[i]);
free(tokens[i]);
}
return 0;
}
#endif
U can do something like this too.
int main ()
{
char str[] ="test string.";
char * temp1;
char * temp2;
temp1 = strtok (str," ");
temp2 = strchr(str, ' ');
if (temp2 != NULL)
temp2++;
printf ("Splitted string :%s, %s\n" , temp1 , temp2);
return
}
I'm working on a function that takes filepaths and dices them up into smaller sections.
For example, if the input parameter was "cd mypath/mystuff/stack/overflow/string", I want to be able to return "cd" "mypath", "mystuff", "stack", "overflow", and "string" in succession.
While I could simply continually use "getchar", appending the results to an ever-increasing string, stopping when getchar returns a '/', I feel like there must be a more elegant way to achieve the same functionality.
Any ideas?
You can use the char * strtok ( char * str, const char * delimiters ); using / as separator.
An example here:
http://www.cplusplus.com/reference/clibrary/cstring/strtok/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s path\n", argv[0]);
exit(EXIT_FAILURE);
}
char* saveptr = NULL;
for (char* str = argv[1]; ; str = NULL) {
char *token = strtok_r(str, "/", &saveptr);
if (token == NULL)
break;
printf("%s\n", token);
}
return 0;
}
Example
clang -Wall *.c && ./a.out mypath/mystuff/stack/overflow/string
mypath
mystuff
stack
overflow
string
Here's an example of how sscanf() can stop after a certain character.
sscanf("abcd/efgh", "%[^/]", &buf);
printf("%s\n", buf);
Should produce
abcd
EDIT: You could try something like this to advance sscanf() input. I have not tested this for various edge cases, but it should get the idea across.
char *str = "abcd/efgh/ijk/xyz";
while (sscanf(str, "%[^/]%n", &buf, &n)) {
printf("%s\n", buf);
str += n;
if (*str == '\0')
break;
++str;
}
should produce
abcd
efgh
ijk
xyz
Here is an example using regcomp, regexec. Compile and run it with the first arg being the character you are searching on, while the second arg is the string to search.
For example, a.out X abcXdefXghiXjkl will print abc def ghi jkl on separate lines.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <regex.h>
int
main(int argc, char *argv[]) {
int len;
char *cp;
char *token;
regex_t preg;
regmatch_t match;
if (regcomp(&preg, argv[1], REG_EXTENDED) != 0) {
return 0;
}
for (cp = argv[2]; *cp != '\0'; cp += len) {
len = (regexec(&preg, cp, 1, &match, 0) == 0) ? match.rm_eo : strlen(cp);
token = malloc(len);
strncpy(token, cp, len);
printf("%s\n", token);
}
return 0;
}