How can I stop scanf-ing input after a certain character? - c

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;
}

Related

Segmentation error when convert string to array of strings in c

I want to convert a string to an array of strings and I get an error
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int count = 0;
char *str = argv[1];
char *token, *last;
char **arr_str = calloc(9999, sizeof(char*));
token = strtok_r(str, " ,", &last);
arr_str[count] = strcpy(calloc(strlen(token), sizeof(char)), token);
while (token != NULL) {
count++;
token = strtok_r(NULL, " ", &last);
arr_str[count] = strcpy(calloc(strlen(token), sizeof(char)), token);
printf("%s", arr_str[count - 1]);
}
printf("------------");
while(arr_str[count])
printf("%s", arr_str[count--]);
exit (0);
}
how to allocate memory for a string and make a pointer to it from an array?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
/* always check argument count */
if(argc < 2) {
printf("Not enough arguments given\n");
return 1;
}
int count = 0;
char *str = malloc(strlen(argv[1] + 5));
memcpy(str, argv[1], strlen(argv[1]));
char *token, *last;
char **arr_str = calloc(9999, sizeof(char*));
token = strtok_r(str, " ,", &last);
while ((token = strtok_r(NULL, " ", &last)) != NULL) {
count++;
/* sizeof(char) is always 1 and is redundant unless you are on
a obscure platform that it returns other than 1
which shouldnt exist in modern world
*/
arr_str[count] = malloc(strlen(token) + 1);
strcpy(arr_str[count], token);
}
printf("------------");
while(arr_str[count])
printf("%s", arr_str[count--]);
exit (0);
}
strtok is destructive meaning it edits strings it encounters, it tried to edit argv which resulted in a segmentation error.
I also edited code to follow better practices and edited formatting.
You need memory for elements of the arr_str.
calloc(9999) while not great if this not going to end up in a serious application it's not a issue.
sizeof(char) should always return 1 on a normal modern system unless you are on extremely obscure system
Use puts(char* s) if you don't need string formatting.
You should do input validation.

C dynamic length char array

I wanted to write a program in C that will accept a line of any length from stdin and display it or apply any function to that string. For this to work I will need a String (char []) with dynamic length.
This is how I did it:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv){
char *line;
line = malloc(10);
line[0] = '\0';
char *str = malloc(10);
fprintf(stdout, "Please enter your line:\n");
while(fgets(str, 10, stdin)){
//check for line break
if(str[strlen(str)-1] == '\n'){
str[strlen(str) - 1] = '\0';
strcat(line, str);
break;
}
strcat(line, str);
line = realloc(line, strlen(line) + 10);
str = realloc(str, strlen(str) + 10);
}
fprintf(stderr, "you entered %s\n", line);
//just for testing
/*
fprintf(stderr, "\n str= %s \n", str );
fprintf(stderr, "\n line= %s \n", line);
*/
free(line);
free(str);
exit(EXIT_SUCCESS);
}
However, this looks awful. I need two char arrays. In char *str I will write input from stdin and concatenate it to char *line. str will only hold up to 10Bytes of chars and because of that I need to concatenate everything to line.
Is there a cleaner way to save the output from stdin in this case and apply some function on it? Am I doing this wrong? Can it be done without malloc and realloc?
This an example. You need to add the malloc & realloc result checking (I did not for the sake of simplicity)
#include <stdio.h>
#include <stdlib.h>
#define CHUNK 32
char *readline(void)
{
size_t csize = CHUNK;
size_t cpos = 0;
char *str = malloc(CHUNK);
int ch;
while((ch = fgetc(stdin)) != '\n' && ch != '\r')
{
str[cpos++] = ch;
if(cpos == csize)
{
csize += CHUNK;
str = realloc(str, csize);
}
}
str[cpos] = 0;
return str;
}
int main()
{
printf("\n%s\n", readline());
return 0;
}
working example: https://onlinegdb.com/Sk9r4gOYV
You should also free the allocated memory when not needed anymore.

strtok when process two strings at same time

New in C and pretty confused about how to deal with several strings at the same time using strtok, for a simply example, I want to use strtok to extract the number and compare then.
#include <stdio.h>
#include <string.h>
int main()
{
char s1[100]="11.54";
char s2[100]="12.55";
const char tok[2]=".";
char* token1=strtok(s1,tok);
char* token2=strtok(s2,tok);
while(token1 !=NULL && token2 !=NULL){
int temp=strcmp(token1,token2);
if(temp==0){
token1=strtok(NULL,tok);
token2=strtok(NULL,tok);
}
else if(temp<0){
printf("%d\n",-1);
return;
}
else{
printf("%d\n",1);
return;
}
}
if(token1 !=NULL){
printf("%d\n",1);
return;
}
if(token2 !=NULL){
printf("%d\n",-1);
return;
}
printf("%d\n",0);
return 0;
}
But when I use the strtok, the strtok(NULL,token)will point to the current string and will do like: 11->12>55->NULL and skip the 54
How could I deal with such situation? Thanks!!
Do not use strtok(). The documentation will tell you strtok() is not reentrant (i.e. should not be used across threads), but perhaps less obvious is the fact that the reason it is not reentrant is because it uses an internal save variable to remember where it's got to. That means you also can't use two instances at once. Instead use strtok_r() or failing that strsep() might work.
strtok_r() is just like strtok, save that you pass it a char ** (i.e. a pointer to char *) where it can save where it's got to.
The GNU libc manual page gives a good example of using a nested strtok_r which is what you are trying to do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(int argc, char *argv[])
{
char *str1, *str2, *token, *subtoken;
char *saveptr1, *saveptr2;
int j;
if (argc != 4) {
fprintf(stderr, "Usage: %s string delim subdelim\n",
argv[0]);
exit(EXIT_FAILURE);
}
for (j = 1, str1 = argv[1]; ; j++, str1 = NULL) {
token = strtok_r(str1, argv[2], &saveptr1);
if (token == NULL)
break;
printf("%d: %s\n", j, token);
for (str2 = token; ; str2 = NULL) {
subtoken = strtok_r(str2, argv[3], &saveptr2);
if (subtoken == NULL)
break;
printf(" --> %s\n", subtoken);
}
}
exit(EXIT_SUCCESS);
}

Delimiting Char Array for Three Variables

I'm writing a program to parse a command-line argument into three different parts: host name, file path, and file name, however I am unsure of how to parse a single command-line argument and store the separate parts in three different variables.
I need each portion to create a socket on the client-side of my program. So far I've been able to parse the host name portion, but I get stuck after that.
Is there a way that, after parsing a portion of the string?
EDIT:
The string I'm trying to parse is something like camelot.cba.csuohio.edu/~yourloginid/filename.txt
Here's my code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char *argv[])
{
int i, sk;
char buf[256], temp[256];
struct sockaddr_in remote;
struct hostent *hp;
if(argc != 2)
{
printf("Invalid number of arguments. Program terminating...");
exit(1);
}
sk = socket(AF_INET, SOCK_STREAM, 0);
remote.sin_family = AF_INET;
strcpy(buf, argv[1]);
for(i = 0; i < strlen(buf); i++)
{
if(buf[i] == '/')
break;
temp[i] = buf[i];
}
hp = gethostbyname(temp);
return 0;
}
EDIT:
I've implemented a while loop to achieve what I'm looking for, but I feel like it's sloppy. Is there a way I can improve it?
while(tk != NULL)
{
if(c == 0)
strcpy(host, tk);
else if(c == 1)
strcpy(path, tk);
else
strcpy(fname, tk);
c++;
tk = strtok(NULL, "/");
}
char st[] = "camelot.cba.csuohio.edu/~yourloginid/filename.txt";
char *host, *path, *fname;
char *ch[3];
for (int i=0; i < 3; ++i) {
ch[i] = strtok(st, "/");
(if ch[i] == NULL) break;
printf("%s\n", ch[i]);
}
if (ch[0] != NULL) {
host = ch[0];
}
if (ch[1] != NULL) {
path = ch[1];
}
if (ch[2] != null) {
path = ch[2];
}
Output:
camelot.cba.csuohio.edu
~yourloginid
filename.txt
You can parse that with strtok
A rough example for you case would be
const char s[2] = "/";
char *token;
/* get the first token */
token = strtok(argv[1], s);
/* walk through other tokens */
while( token != NULL )
{
printf( " %s\n", token );
token = strtok(NULL, s);
}
I didn't compile it but I hope you can use it as an example.
Here you have a complete example of how to use it
http://www.tutorialspoint.com/c_standard_library/c_function_strtok.htm
Hope this helps.
When you know the delimiters, never forget you have simple pointer arithmetic available to you to split/parse any sting. strtok and sscanf are fine tools, but you can do the same thing manually. Here is a short example to add to your list:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXS 128
int main (int argc, char **argv) {
if (argc < 2 ) {
fprintf (stderr, "Error: insufficient input, usage: %s host,path,file\n", argv[0]);
return 1;
}
char *line = strdup (argv[1]); /* make a copy of argument string */
if (!line) {
fprintf (stderr, "error: strdup memory allocation/copy failed.\n");
return 1;
}
char *p = line; /* pointer to the argument string */
char *sp = NULL; /* pointer to use as start pointer */
char host[MAXS] = {0}; /* variables to hold tokens */
char path[MAXS] = {0};
char file[MAXS] = {0};
while (*p && *p != ',') p++; /* find the first ',' */
*p++ = 0; /* null-terminate, advance pointer */
strcpy (host, line); /* read/copy host name */
sp = p; /* set start pointer at current pos */
while (*p && *p != ',') p++; /* find next ',' */
*p++ = 0; /* null-terminate, advance pointer */
strcpy (path, sp); /* read/copy path */
strcpy (file, p); /* pointer on file, read/copy file */
printf ("\n host: %s\n path: %s\n file: %s\n\n", host, path, file);
free (line); /* free memory allocate by strdup */
return 0;
}
Output
$ ./bin/split_host_path_file hostname,pathname,filename
host: hostname
path: pathname
file: filename
Updated to prevent potential read beyond end of line with p.
you can also parse with strtok_r as follows, since strtok is not thread safe.
const char *delim="/";
char *str, *savePtr;
char hosts[3][32];
int i;
for(i=0,str=strtok_r(argv[1], delim, &savePtr);(str!=NULL);str=strtok_r(NULL, delim, &savePtr), i++)
{
print("%s\n", str);
strcpy((char *)host[i], (const char *)str);
}
access host array elements, as it will contain the indexed values delimited by "/"

Delimiting path using strtok()

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

Resources