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.
Related
I am trying to figure out if there is a way to assign a string which includes null terminators within the string as well as the end in one go. I am able to assign it manually, char by char, but is there a way to do this in one go? I looked for a similar question but couldn't find one.
Where I'm stuck.
EX:
char *argvppp = (char *)malloc(14);
argvppp = "mine\0-c\0/10\0/2.0\0"; // forward slash before the 10 after the null terminator
When I try to read from argvppp, like this:
printf("%s\n", argvppp);
printf("%s\n", argvppp+5);
printf("%s\n", argvppp+8);
printf("%s", argvppp+11);
This is what I get:
mine
-c
/10
[blank]
And when I try to just not escape it like this:
argvppp = "mine\0-c\010\0/2.0\0"; // no forward slash before the 10
This is what I get:
mine
-c
[blank]
.0
Is there a reliable way to do this without having to manually assign assign char by char?
What does work:
argvppp[0] = 'm';
argvppp[1] = 'i';
argvppp[2] = 'n';
argvppp[3] = 'e';
argvppp[4] = '\0';
argvppp[5] = '-';
argvppp[6] = 'c';
argvppp[7] = '\0';
argvppp[8] = '1';
argvppp[9] = '0';
argvppp[10] = '\0';
argvppp[11] = '2';
argvppp[12] = '.';
argvppp[13] = '0';
argvppp[14] = '\0';
I'm just wondering if there would be a way around this manual method.
Use memcpy()
char *argvppp = malloc(sizeof "mine\0-c\0/10\0/2.0\0");
memcpy(argvppp, "mine\0-c\0/10\0/2.0\0", sizeof "mine\0-c\0/10\0/2.0\0");
When you don't put a / between \0 and 10, you get a single character \010. You can solve this by splitting the string literal apart.
"mine\0-c\0" "10\0/2.0\0"
Adjacent string literals are automatically concatenated.
= does not copy the string only assigns the pointer to string literal to your variable. The memory allocated is lost. string literals cannot be modified as it invokes UB (undefined behaviour)
You need to copy the string literal into the memory referenced by te pointer:
define STR "mine\0-c\0/10\0/2.0\0";
char *argvppp = malloc(sizeof(STR));
memcpy(argvppp, STR, sizeof(STR));
you can also:
char argvppp[] = "mine\0-c\0/10\0/2.0\0";
You could try something like this where your data is clearly visible.
int main() {
char *segs[] = { "mine", "-c", "10", "2.0" };
char buf[ 50 ]; // long enough?!
char *at = buf;
for( int i = 0; i < sizeof segs/sizeof segs[0]; i++ )
at += sprintf( at, "%s", seg[i] ) + 1; // NB +1 is beyond '\0'
/* Then examine the contents of 'buf[]' character by character. */
return 0;
}
So i've seen alot of functions like str_replace(str, substr, newstring) but all of them won't work with numbers so i was wondering if anyone had one that would work with both chars and ints or just int ive been looking everywhere and cant figure out a idea on how to write my own.
my goal exactly is to be able to replace a string with a int value in the string not just string with string
below is the function i use to replace strings and it worked just fine
void strrpc(char *target, const char *needle, const char *replacement)
{
char buffer[1024] = { 0 };
char *insert_point = &buffer[0];
const char *tmp = target;
size_t needle_len = strlen(needle);
size_t repl_len = strlen(replacement);
while (1) {
const char *p = strstr(tmp, needle);
// walked past last occurrence of needle; copy remaining part
if (p == NULL) {
strcpy(insert_point, tmp);
break;
}
// copy part before needle
memcpy(insert_point, tmp, p - tmp);
insert_point += p - tmp;
// copy replacement string
memcpy(insert_point, replacement, repl_len);
insert_point += repl_len;
// adjust pointers, move on
tmp = p + needle_len;
}
// write altered string back to target
strcpy(target, buffer);
}
You can turn an integer into a string by "printing" it to a string:
int id = get_id();
char idstr[20];
sprintf(idstr, "%d", id);
Now you can
char msg[1024] = "Processing item {id} ...";
strrpc(msg, "{id}", idstr);
puts(msg);
But note that the implementation of strrpc you found will work only if the string after replacement is shorter than 1023 character. Also note the the example above could more easily be written as just:
printf("Processing item %d ...\n", get_id());
without the danger of buffer overflow. I don't know what exactly you want to achieve, but perhaps string replacement is not the best solution here. (Just sayin'.)
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.
One thing I love about Python and PHP is the ability to make a string from array easily:
Python: ', '.join(['a', 'b', 'c'])
PHP: implode(', ', array('a', 'b', 'c'));
However, I was wondering if anybody had an intuitive and clear way to implement this in C. Thanks!
Sure, there are ways - just nothing built-in. Many C utility libraries have functions for this - eg, glib's g_strjoinv. You can also roll your own, for example:
static char *util_cat(char *dest, char *end, const char *str)
{
while (dest < end && *str)
*dest++ = *str++;
return dest;
}
size_t join_str(char *out_string, size_t out_bufsz, const char *delim, char **chararr)
{
char *ptr = out_string;
char *strend = out_string + out_bufsz;
while (ptr < strend && *chararr)
{
ptr = util_cat(ptr, strend, *chararr);
chararr++;
if (*chararr)
ptr = util_cat(ptr, strend, delim);
}
return ptr - out_string;
}
The main reason it's not built in is because the C standard library is very minimal; they wanted to make it easy to make new implementations of C, so you don't find as many utility functions. There's also the problem that C doesn't give you many guidelines about how to, for example, decide how many elements are in arrays (I used a NULL-array-element terminator convention in the example above).
For example there is such a function in GLib: g_strjoin and g_strjoinv. Probably any bigger library has such functions.
The easiest way is to use such libraries and be happy. It's also not too hard to write this by yourself (look at the other answers). The "big" problem is just that you have to be careful while allocating and freeing those strings. It's C ;-)
Edit: I just see that you used in both examples arrays. So just that you know: g_strjoinv is what you asked for.
I found a function that does this in ANSI C here. I adapted it and added a seperator argument. Make sure to free() the string after using it.
char* join_strings(char* strings[], char* seperator, int count) {
char* str = NULL; /* Pointer to the joined strings */
size_t total_length = 0; /* Total length of joined strings */
int i = 0; /* Loop counter */
/* Find total length of joined strings */
for (i = 0; i < count; i++) total_length += strlen(strings[i]);
total_length++; /* For joined string terminator */
total_length += strlen(seperator) * (count - 1); // for seperators
str = (char*) malloc(total_length); /* Allocate memory for joined strings */
str[0] = '\0'; /* Empty string we can append to */
/* Append all the strings */
for (i = 0; i < count; i++) {
strcat(str, strings[i]);
if (i < (count - 1)) strcat(str, seperator);
}
return str;
}
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;
}