Copying and Merging two strings in C - c

Note: Follow Alk's solution as a guide on your implemetation
I want to create a function that will let the user insert a directory where output files would be saved. My output files will have a static name so I just need the path.
I want to read the path from the user and append it before the name of the output file. So it would be like this:
output name (generated by another function) outLogFile = "outLogFile.log"
user input = D:\Datasets\some_folder\more_folders
RESULT = D:\Datasets\some_folder\more_folders\outLogFile.log
The way I am doing it, I insert the output name on a temp, use strcpy to copy the file path into outLogFile and strcat to append temp into outLogFile.
Is there a simpler way to do it? A way to merge the two strings into my outLogFile without the use of the temp? A single command to copy the string of path_file before the ouLogFile string, and save it into outLogFile?
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main ()
{
char user_input[100], *path_file,*temp;
char *outLogFile = "outLogFile.log";
printf("Filepath:\n (!Do not inlude filename!)\n");
gets(user_input);
path_file = (char*)malloc(strlen(user_input)+1);
if (user_input[strlen(user_input) - 1]!='\\')
{
strcpy(path_file, user_input);
strcat(path_file, "\\");
}
else
{
strcpy(path_file, user_input);
}
temp = outLogFile;
strcpy(outLogFile, path_file);
strcat(outLogFile, temp);
printf("%s\n%s\n", path_file,outLogFile);
system("pause");
return 0;
}
EDIT: I could use the user_input and path_file to malloc the outLogFile and strcpy strcat the string as follows
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main ()
{
char user_input[100];
char *outLogFile;
char *path_file = "outLogFile.log";
printf("Filepath:\n (!Do not inlude filename!)\n");
fgets(user_input,sizeof(user_input), stdin);
printf("%c\n",user_input[strlen(user_input) - 1]);
outLogFile = (char*)malloc(strlen(user_input)+strlen(path_file));
if (user_input[strlen(user_input) - 1]!='\\')
{
strcpy(outLogFile,user_input);
strcat(outLogFile, "\\");
strcat(outLogFile,path_file);
}
else
{
strcpy(outLogFile,user_input);
strcat(outLogFile,path_file);
}
printf("%s",outLogFile);
system("pause");
return 0;
}
However this code takes the \n by hitting the return button and inserts it between the two strings

To prefix a string by another string and store the result in a new string the most flexible generic approach would be to use dynamic memory allocation like this:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
char * ps1 = "Hello";
char * ps2 = " World";
size_t length_total = strlen(ps1) + strlen(ps2);
char * ps3 = malloc((length_total + 1) * sizeof *ps3); /* One more for
the 0-terminator. */
if (NULL == ps3)
{
perror("malloc() failed");
exit(EXIT_FAILURE);
}
strcpy(ps3, ps1);
strcat(ps3, ps2);
/* Use ps3. */
puts(ps3);
/* Clean up. */
free(ps3);
return EXIT_SUCCESS;
}
In your particular case where the code provide a default filename without a path and the use-case is to allow prefixing the filename on run-time one might approach this in a simpler way like this.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define LOGFILENAME "some.log"
int main(void)
{
char logfilepath[PATH_MAX] = LOGFILENAME; /* Just to make sure. */
char dir[PATH_MAX] = "";
if (NULL == fgets(dir, sizeof dir, stdin))
{
if (ferror(stdin))
{
perror("fgets() failed");
exit(EXIT_FAILURE);
}
}
dir[strcspn(dir, "\n\r")] = 0;
{
size_t length_dir = strlen(dir);
if (length_dir > 0 && '/' != dir[length_dir - 1])
{
if (PATH_MAX < length_dir)
{
errno = EINVAL;
perror("'dir' to long");
exit(EXIT_FAILURE);
}
strcat(dir, "/");
++length_dir;
}
{
size_t length_total = length_dir + strlen(logfilepath);
if (PATH_MAX < length_total)
{
errno = EINVAL;
perror("'dir/filename' to long");
exit(EXIT_FAILURE);
}
}
}
strcpy(logfilepath, dir);
strcat(logfilepath, LOGFILENAME);
/* Use logfilepath, . */
puts(logfilepath);
return EXIT_SUCCESS;
}
To not trick this using the #define and to not use a third variable go for shifting:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(void)
{
char logfilepath[PATH_MAX] = "some.log";
{
char dir[PATH_MAX] = "";
if (NULL == fgets(dir, sizeof dir, stdin))
{
if (ferror(stdin))
{
perror("fgets() failed");
exit(EXIT_FAILURE);
}
}
dir[strcspn(dir, "\n\r")] = 0;
{
size_t length_filepath = strlen(logfilepath);
size_t length_dir = strlen(dir);
if (length_dir > 0 && '/' != dir[length_dir - 1])
{
if (PATH_MAX < length_dir)
{
errno = EINVAL;
perror("'dir' to long");
exit(EXIT_FAILURE);
}
strcat(dir, "/");
++length_dir;
}
if (PATH_MAX < (length_dir + length_filepath))
{
errno = EINVAL;
perror("'dir/filename' to long");
exit(EXIT_FAILURE);
}
memmove(logfilepath + length_dir, logfilepath, length_filepath + 1);
memcpy(logfilepath, dir, length_dir);
}
}
/* Use logfilepath, . */
puts(logfilepath);
return EXIT_SUCCESS;
}

You may use sprintf() .
Following is the declaration for sprintf() function.
int sprintf(char *str, const char *format, ...)
For Example :
sprintf(outLogFile, "%s%s", path_file, outLogFile);
You have to care about '\0' character of first string now.

Related

Listing files in a directory into array in C using strcpy and strncpy

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
int main()
{
struct dirent *de;
char *filesList[258];
DIR *dr = opendir("C:\\Users\\Asus\\Desktop\\Training-Process-of-YOLO-architecture\\annotations");
if (dr == NULL)
{
printf("could not open folder");
return 0;
}
int i = 0;
while ((de = readdir(dr)) != NULL)
{
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
{
}
else
{
filesList[i] = (char *)malloc(strlen(de->d_name) + 1);
strcpy(filesList[i], de->d_name); // strncpy add some unnecessary values at the end of some array elements
i++;
}
}
closedir(dr);
for (int i = 0; i < 256; i++)
{
printf("%s\n", filesList[i]);
}
return 0;
}
strncpy adds some junk values at the end of some array elements as follows.
2077.xml\ProgramΦ_U═≥
2080.xml$G
2083.xmlam FilesΦ_U═≥
2086.xmlll\ModulΦ_U═≥
2089.xmlstem32\WΦ_U═≥
2095.xml\v1.0\MoΦ_U═≥
2098.xml\ProgramΦ_U═≥
Here I'm reading xml files and adding them to an array. Above you can see some junk values are coming at the end of the elements. This doesn't happen with strcpy(). Can someone explain this behaviour ?
The posted code needs to pay attention to the fields:
/* Length of name without \0 */ size_t d_namlen;
/* File type */ int d_type;
Notice there is a 'name length' field that can be used when copying the directory name.
Notice the file type that can be used to determine if the current file is a 'normal' file or something else.

Function concatenates extra characters

I'm writing a function that should read a string of unknown length. I can use read, malloc, open, close and my own library that behaves like a normal one. It includes functions from the standard library and a few additional.
The problem is that fill_append() copies string from gnl->buf to *line incorrectly. In addition to copying the string itself, it adds other characters. Functions with ft_ prefix work accurately.
int fill_append(t_gnl* gnl, char** line)
{
char* append;
int index;
char* sub;
char* tmp;
append = ft_strchr(gnl->buf, '\n');
if (append == NULL)
{
*line = ft_strdup(gnl->buf);
return (0);
}
index = (int)(append - gnl->buf);
sub = ft_strsub(gnl->buf, 0, index);
tmp = ft_strnew(ft_strlen(*line) + ft_strlen(sub) + 1);
ft_strcpy(tmp, *line);
ft_strcat(tmp, sub);
*line = tmp;
// tmp = (char *)malloc(ft_strlen(*line) + ft_strlen(sub) + 1);
// memcpy(tmp, *line, ft_strlen(*line));
// memcpy(tmp + ft_strlen(*line), sub, ft_strlen(sub) + 1);
// *line = tmp;
gnl->buf = ft_strdup(&gnl->buf[index + 1]);
return (1);
}
int read_fd(t_gnl* gnl, char** line)
{
int bsize;
char* tmp;
while ((bsize = read(gnl->fd, gnl->buf, BUFF_SIZE)))
{
gnl->buf[bsize] = '\0';
if (ft_strchr(gnl->buf, '\n') == NULL)
{
tmp = *line;
*line = ft_strjoin(*line, gnl->buf);
free(tmp);
free(gnl->buf);
gnl->buf = ft_strnew(BUFF_SIZE);
}
else
return (fill_append(gnl, line));
}
if (bsize == 0 && *line[0] == 0)
{
free(gnl->buf);
return (0);
}
return (1);
}
int get_next_line(const int fd, char** line)
{
static t_gnl* gnl;
if (fd < 0 || line == NULL)
return (-1);
*line = ft_strnew(0);
if (gnl)
if (gnl->buf)
if (fill_append(gnl, line))
return (1);
if (!gnl)
gnl = (t_gnl*)malloc(sizeof(t_gnl));
gnl->buf = ft_strnew(BUFF_SIZE);
gnl->fd = fd;
return (read_fd(gnl, line));
}
For example, with input is sfesefsefsefwefsefsefsef
sfesefsefsefwefsefsefsef function returns sfesefsefsefwefsefsefsef1
sfesefsefsefwefsefsefsef!
Header:
#ifndef GET_NEXT_LINE_H
#define GET_NEXT_LINE_H
#define BUFF_SIZE 150
#include <unistd.h>
#include <stdlib.h>
#include "libft/libft.h"
typedef struct s_gnl
{
char* buf;
int fd;
} t_gnl;
int get_next_line(const int fd, char** line);
#endif
Main:
#include <stdlib.h>
#include <fcntl.h>
#include "get_next_line.h"
#include "libft/libft.h"
int main(void)
{
int fd;
char* line;
fd = open("gnl.txt", O_RDONLY);
while (get_next_line(fd, &line))
{
ft_putendl(line);
ft_strdel(&line);
}
close(fd);
}

How do I insert the result of system() in a string?

I'd like to insert the result of the command system("echo %username%"); in a string, but I can't figure out how I could do it in C. Can someone please help me?
Adapted from this C++ solution and a little bit more flexible than August Karlstroms answer you can do something like this:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define S_SIZE 128
char * exec(const char* cmd)
{
FILE* pipe = _popen(cmd, "r"); // open a pipe to the command
if (!pipe) return NULL; // return on Error
char buffer[S_SIZE];
int size = S_SIZE;
char * result = NULL;
while (fgets(buffer, 128, pipe) != NULL)
{
result = realloc(result, size); // allocate or reallocate memory on the heap
if (result && size != S_SIZE) // check if an error occured or if this is the first iteration
strcat(result, buffer);
else if (result)
strcpy(result, buffer); // copy in the first iteration
else
{
_pclose(pipe);
return NULL; // return since reallocation has failed!
}
size += 128;
}
_pclose(pipe);
return result; // return a pointer to the result string
}
int main(void)
{
char* result = exec("echo %username%");
if (result) // check for errors
{
printf("%s", result); // print username
free(result); // free allocated string!
}
}
Use the POSIX function popen:
#include <stdio.h>
#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])
int main(void)
{
FILE *f;
char s[32];
const char *p;
f = popen("echo august", "r");
p = fgets(s, LEN(s), f);
if (p == NULL) {
s[0] = '\0';
}
pclose(f);
puts(s);
return 0;
}

How to use Quicksort for struct array of strings in ANSI C

I have a struct of strings with 3 million lines. I am trying to sort the file like:
aaaaa
aaaab
aaacc
And so on.
I was trying to do bubblesort. I tried it with 10 lines and it worked, but when I tried the whole 3 million lines file it took over 30 minutes and was still processing. I decided to try quicksort. However, I am running into a problem where it says:
expected 'const char **' but argument is of type 'struct lines *'
How can I fix this? Here is what I am doing:
#include<stdio.h>
#include<string.h>
#include <stdlib.h>
#include <math.h>
#include <stdbool.h>
#include <ctype.h>
void swap_str_ptrs(char const **arg1, char const **arg2)
{
const char *tmp = *arg1;
*arg1 = *arg2;
*arg2 = tmp;
}
void quicksort_strs(char const *args[], unsigned int len)
{
unsigned int i, pvt=0;
if (len <= 1)
return;
// swap a randomly selected value to the last node
swap_str_ptrs(args+((unsigned int)rand() % len), args+len-1);
// reset the pivot index to zero, then scan
for (i=0;i<len-1;++i)
{
if (strcmp(args[i], args[len-1]) < 0)
swap_str_ptrs(args+i, args+pvt++);
}
// move the pivot value into its place
swap_str_ptrs(args+pvt, args+len-1);
// and invoke on the subsequences. does NOT include the pivot-slot
quicksort_strs(args, pvt++);
quicksort_strs(args+pvt, len - pvt);
}
void main()
{
FILE *dnaFile=fopen("hs_alt_HuRef_chr2.fa", "r"); //file im reading
typedef struct lines
{
char lines[100]; //size of each line
} lines;
int i = 0;
char buf[256];
static lines myDNA[3354419]; //creates the 3m spots for all lines
while (fgets (buf, sizeof(buf), dnaFile))
{
if (i > 0)
strcpy(myDNA[i].lines, buf); //inserting each line into the struct array
i++;
}
// this is the bubblesort approach, works, but it takes too lon
/**int a;
int total;
char temp[150];
char report[100][150];
for(a=0; a<3354419; a++)
{
for(total=a+1; total<=3354419; total++)
{
if(strcmp(myDNA[a].lines,myDNA[total].lines)>0)
{
strcpy(temp,myDNA[a].lines);
strcpy(myDNA[a].lines,myDNA[total].lines);
strcpy(myDNA[total].lines,temp);
}
}
}*/
quicksort_strs(myDNA, 3354419); //attempt at quicksort, which crashes
}
USING QSORT
#include<stdio.h>
#include<string.h>
#include <stdlib.h>
#include <math.h>
#include <stdbool.h>
#include <ctype.h>
int compare_function(const void *a,const void *b) {
return (strcmp((char *)a,(char *)b));
}
void main()
{
FILE *dnaFile=fopen("hs_alt_HuRef_chr2.fa", "r"); //file with 3 million lines
typedef struct lines
{
char lines[100];
} lines;
int i = 0;
char buf[256];
static lines myDNA[3354419]; // array holding the 3 million lines
while (fgets (buf, sizeof(buf), dnaFile))
{
if (i > 0)
strcpy(myDNA[i].lines, buf); //putting each line into array
i++;
}
qsort(myDNA, 1000, 100, compare_function); //qsort works for first 1k lines, after, messed up
int a;
for (a = 0; a < 1000; a++){
printf("%s", myDNA[a].lines); //printing lines
}
}
I have modified the question code a bit. From my testing, the following code appears to function as needed (as stated by the question).
#include <stdio.h> // printf(), fprintf(), fclose(), feof(), fgets(), fopen()
#include <string.h> // memset(), strcmp(), strdup()
#include <stdlib.h> // malloc(), qsort(), free()
#include <errno.h> // errno, ENOMEM, EIO
#define MAX_FILE_LINES 3354419
#define MAX_LINE_SIZE (255+1)
int compare_function(const void *a, const void *b)
{
return(strcmp(*(const char **)a, *(const char **)b));
}
int main(int argC, char *argV[])
{
int rCode = 0;
char *filePath = "hs_alt_HuRef_chr2.fa";
FILE *dnaFile = NULL;
char **myDNA = NULL;
int myDNAcnt = 0;
int index;
/** Allow user to specify the file path on the command-line. **/
if(argC > 1)
filePath=argV[1];
/** Allocate an array (to hold the 3 million lines). **/
errno=0;
myDNA=malloc(MAX_FILE_LINES * sizeof(*myDNA));
if(NULL == myDNA)
{
rCode=errno?errno:ENOMEM;
fprintf(stderr, "malloc() failed. errno:%d\n", errno);
goto CLEANUP;
}
memset(myDNA, 0, MAX_FILE_LINES * sizeof(*myDNA));
/** Open the file. **/
errno=0;
dnaFile=fopen(filePath, "r");
if(NULL == dnaFile)
{
rCode=errno;
fprintf(stderr, "fopen() failed to open \"%s\". errno:%d\n", filePath, errno);
goto CLEANUP;
}
/** Read the file into the array, allocating dynamic memory for each line. **/
for(myDNAcnt=0; myDNAcnt < MAX_FILE_LINES; ++myDNAcnt)
{
char buf[MAX_LINE_SIZE];
char *cp;
if(NULL == fgets(buf, sizeof(buf), dnaFile))
{
if(feof(dnaFile))
break;
rCode=EIO;
fprintf(stderr, "fgets() failed.\n");
goto CLEANUP;
}
cp=strchr(buf, '\n');
if(cp)
*cp='\0';
errno=0;
myDNA[myDNAcnt] = strdup(buf);
if(NULL == myDNA[myDNAcnt])
{
rCode=errno;
fprintf(stderr, "strdup() failed. errno:%d\n", errno);
goto CLEANUP;
}
}
/** Sort the array. **/
qsort(myDNA, myDNAcnt, sizeof(*myDNA), compare_function);
/** Print the resulting sorted array. **/
for(index=0; index < myDNAcnt; index++)
{
printf("%8d: %s\n",index, myDNA[index]); //printing lines
}
CLEANUP:
/** Close the file. **/
if(dnaFile)
fclose(dnaFile);
/** Free the array. **/
if(myDNA)
{
for(index=0; index < myDNAcnt; index++)
{
free(myDNA[index]);
}
free(myDNA);
}
return(rCode);
}

C programming, calling a function

I am new to C and i am trying to iteratively call line in stream and check to see if it contains my search string or if it is null. I cant figure out how to make this check, i get a warning saying [Warning] comparison between pointer and integer or [Warning] assignment makes pointer from integer without a cast whenever i try and do this. can anyone help? thanks.
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
FILE *fpntr;
char *file_pathname, *first_line;
if (argc != 2) {
fprintf(stderr, "usage: %s FILE\n", argv[0]);
return EXIT_FAILURE;
}
file_pathname = argv[1];
if ((fpntr = fopen(file_pathname, "r")) == NULL ) {
fprintf(stderr, "Error opening file %s: %s\n", file_pathname, strerror(errno));
return EXIT_FAILURE;
} else {
grep_stream();
fclose(fpntr);
}
return EXIT_SUCCESS;
}
int grep_stream(FILE *fpntr, char *string, char *file_pathname) {
//warning is on next line
while ((? = get_next_line(fpntr)) == NULL ) {
perror("Error reading line");
exit(EXIT_FAILURE);
}
elseif()
{
printf("First line in : %s \n %s", file_pathname, string);
}
}
char *get_next_line(FILE *fpntr) {
char *buff = malloc(101);
int pos = 0;
int next;
while ((next = fgetc(fpntr)) != '\n' && next != EOF) {
buff[pos++] = next;
}
buff[pos] = '\0';
if (buff != NULL ) {
return buff;
} else
return NULL ;
}
Remember that C code is compiled top-to-bottom. The function get_next_line isn't declared by the time the while line is read.
Either move get_next_line's definition to before main's, or forward-declare it by saying:
char *get_next_line(FILE *fpntr);
beforehand. The reason that you're getting a warning instead of an error is that undeclared functions are assumed to return int and no assumptions are made about their parameters. That is, they have the type int().
Also, properly format your code for both your sake and of those who will be answering your questions (or working with you.)
add a * to the pointer of integer to convert it from pointer to integer
... i am trying to iteratively call line in stream...
Why not use fgets()?
Secondly, to match a substring in a string, you can use strstr()
Please use standard C library instead of re-inventing the wheel. It saves the day usually.
#include <assert.h> // I'm too dumb to program without assertions!
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
//#include <unistd.h> I prefer stdlib.h, couldn't see any need for non-portable header...
#define MAX_LINE (101) // define funky constants in one place for easy changing later.
// declare functions before using them
void grep_stream(FILE *fpntr, char *file_pathname);
char *get_next_line(FILE *fpntr);
int main(int argc, char *argv[]) {
FILE *fpntr;
char *file_pathname;
if (argc != 2) {
fprintf(stderr, "usage: %s FILE\n", argv[0]);
return EXIT_FAILURE;
}
file_pathname = argv[1];
if ((fpntr = fopen(file_pathname, "r")) == NULL ) {
fprintf(stderr, "Error opening file %s: %s\n", file_pathname, strerror(errno));
return EXIT_FAILURE;
}
else {
grep_stream(fpntr, file_pathname);
fclose(fpntr);
}
return EXIT_SUCCESS;
}
void grep_stream(FILE *fpntr, char *file_pathname) {
char* line;
int got_first = 0;
assert(fpntr);
assert(file_pathname); // I worry the guy who wrote main() might be an idiot like me!
//warning is on next line [not anymore!]
while ((line = get_next_line(fpntr)) != NULL ) {
if(!got_first) {
printf("First line in : %s \n%s\n", file_pathname, line);
got_first = 1;
}
// if we're not going to use it, let's not leak memory
free(line);
}
}
char *get_next_line(FILE *fpntr) {
char *buff = malloc(MAX_LINE);
int pos = 0;
int next;
assert(buff != NULL); // wouldn't it be nice to know malloc() worked?
while ((next = fgetc(fpntr)) != '\n' && next != EOF) {
buff[pos++] = (char)next;
assert(pos < (MAX_LINE-1)); // don't want to be right back on SO with a seg fault, eh?
}
buff[pos] = '\0';
if(next == EOF) {
free(buff);
buff = NULL;
}
return buff;
}

Resources