I am writing a program that takes a user's comment. Specifically one that has input outside of /* and */ and also inside. I have written my loop to find the char "/" in my array and I am unsure how to remove it and everything in between it until it appears again. For example if my input was "comment /* this is my comment */" I need to remove the /* */ and contents between. So my output would just be "comment". If there is no "/* and */" it doesn't remove anything. I know I need a loop but how would I write a loop that removes chars in the array until the next "/" appears and removes it as well?
My code is as follow:
#include <stdio.h>
#include <string.h>
void remove_comment(char *s1, char *s2){
for(; *s1 != '\0'; s1++){ //loops through array until null value
if(*s1 == '/'){ //if array has '/' stored
//clear array elements till next '/' and removes it as well
}
else{
return; //do nothing to array
}
strcpy(s2,s1); //copies new modified string to s2 for later use
}
int main(){
char s1[101]; //declares arrays up to 100 in length with room for null character
char s2[101];
printf("Enter a comment: "); //enter a comment
fgets(s1, 100, stdin); // saves comment to array
remove_comment(s1,s2); //calls function
printf("%s", s2); //prints my modified array
return 0;
}
Your code seems to build on a loop exploring the chars of the string. I propose you therefore the following solution:
void remove_comment(char *s1, char *s2)
{
for(int in_comment=0; *s1 ; s1++){ //loops through array until null value
if(!in_comment && *s1 == '/' && s1[1]=='*') { //if array has '/' follewed by '*' stored
in_comment=1; // we enter a comment area
s1++;
}
else if (in_comment) { // if we are in a comment area
if (*s1=='*' && s1[1]=='/') { // we only look for end of comment
in_comment = 0;
s1++;
}
}
else *s2++=*s1; // if we're not in comment, in all other cases we just copy current char
}
*s2='\0'; // don't forget to end the string.
}
It uses a in_comment variable to tell if we are currently exploring chars in the comment (and looking for an end of comment) or not (and looking eventually for a start of comment).
It uses *s1 and s1[1] to access the current and the next char.
It leaves the original string unchanged.
Online demo
I know I need a loop
Could use a loop, or use standard library functions. Consider char *strstr(const char *s1, const char *s2); as a candidate part of a solution.
The strstr function locates the first occurrence in the string pointed to by s1 of the sequence of characters (excluding the terminating null character) in the string pointed to
by s2.
The strstr function returns a pointer to the located string, or a null pointer if the string is not found.
Some untested code to give you an idea.
void remove_comment(const char *src, char *dest){
char *start = strstr(str, "/*"); // Look for beginning
if (start) { // Find the beginning?
char *end = strstr(start + 2, "*/"); // Now search 2 past
if (end) { // Find the end?
memcpy(dest, src, start - src);
strcpy(&dest[start - src], end+2);
return;
}
}
strcpy(dest, src);
}
If you want to avoid library funcitons, I'll pass along a hint
// if(*s1 == '/'){
if(s1[0] == '/' && s1[1] == '*') {
Of course this is insufficient for finding /* */ comments in C code like:
puts("/* this is not a C comment, but a string literal */");
int a = '/*', b = '*/';
// start of comment /* xyz */
Related
I'm working my way through C Programming: A Modern Approach but I'm having a bit of trouble with the last exercise of the chapter on strings.
I'm trying to replace a character in a string with a null character to remove the file_name but I keep getting a bus error and I don't understand what I am doing wrong:
void remove_filename(char *url)
{
char *last_slash;
while (*url++)
if (*url == '/')
last_slash = url;
*last_slash = '\0';
}
The way I see it, I am keeping track of the address of the last slash (in the last_slash pointer). Then I target the object stored at that address and replace it by a null character.
Any feedback as to why my reasoning is false is greatly appreciated!
Initialize last_slash so that you can properly check if a '/' was found after the loop. Restructure the loop to only move the pointer after first checking the value it points to.
#include <stdio.h>
void remove_filename(char *url)
{
char *last_slash = NULL;
for (; *url; url++)
if (*url == '/')
last_slash = url;
if (last_slash)
*last_slash = '\0';
}
int main(void)
{
char buffer[] = "path/to/some/file.data";
printf("<<%s>> -> ", buffer);
remove_filename(buffer);
printf("<<%s>>\n", buffer);
}
Output:
<<path/to/some/file.data>> -> <<path/to/some>>
I am writing my own trim() in C. There is a structure which contains all string values, the structure is getting populated from the data coming from a file which contains spaces before and after the beginning of a word.
char *trim(char *string)
{
int stPos,endPos;
int len=strlen(string);
for(stPos=0;stPos<len && string[stPos]==' ';++stPos);
for(endPos=len-1;endPos>=0 && string[endPos]==' ';--endPos);
char *trimmedStr = (char*)malloc(len*sizeof(char));
strncpy(trimmedStr,string+stPos,endPos+1);
return trimmedStr;
}
int main()
{
char string1[]=" a sdf ie ";
char *string =trim(string1);
printf("%s",string);
return 0;
}
Above code is working fine, but i don't want to declare new variable that stores the trimmed word. As the structure contains around 100 variables.
Is there any way to do somthing like below where I dont need any second variable to print the trimmed string.
printf("%s",trim(string1));
I believe above print can create dangling pointer situation.
Also, is there any way where I don't have to charge original string as well, like if I print trim(string) it will print trimmed string and when i print only string, it will print original string
elcuco was faster. but it's done so here we go:
char *trim(char *string)
{
char *ptr = NULL;
while (*string == ' ') string++; // chomp away space at the start
ptr = string + strlen(string) - 1; // jump to the last char (-1 because '\0')
while (*ptr == ' '){ *ptr = '\0' ; ptr--; } ; // overwrite with end of string
return string; // return pointer to the modified start
}
If you don't want to alter the original string I'd write a special print instead:
void trim_print(char *string)
{
char *ptr = NULL;
while (*string == ' ') string++; // chomp away space at the start
ptr = string + strlen(string) - 1; // jump to the last char (-1 because '\0')
while (*ptr == ' '){ ptr--; } ; // find end of string
while (string <= ptr) { putchar(*string++); } // you get the picture
}
something like that.
You could the original string in order to do this. For trimming the prefix I just advance the pointer, and for the suffix, I actually add \0. If you want to keep the original starting as is, you will have to move memory (which makes this an O(n^2) time complexity solution, from an O(n) I provided).
#include <stdio.h>
char *trim(char *string)
{
// trim prefix
while ((*string) == ' ' ) {
string ++;
}
// find end of original string
char *c = string;
while (*c) {
c ++;
}
c--;
// trim suffix
while ((*c) == ' ' ) {
*c = '\0';
c--;
}
return string;
}
int main()
{
char string1[] = " abcdefg abcdf ";
char *string = trim(string1);
printf("String is [%s]\n",string);
return 0;
}
(re-thinking... is it really O(n^2)? Or is it O(2n) which is a higher O(n)...? I guess depending on implementation)
You can modify the function by giving the output in the same input string
void trim(char *string)
{
int i;
int stPos,endPos;
int len=strlen(string);
for(stPos=0;stPos<len && string[stPos]==' ';++stPos);
for(endPos=len-1;endPos>=0 && string[endPos]==' ';--endPos);
for (i=0; i<=(endPos-stPos); i++)
{
string[i] = string[i+stPos];
}
string[i] = '\0'; // terminate the string and discard the remaining spaces.
}
...is there any way where i don't have to charge original string as well, like if i do trim(string) it will print trimmed string and when i print only string, it will print original string – avinashse 8 mins ago
Yes, though it gets silly.
You could modify the original string.
trim(string);
printf("trimmed: %s\n", string);
The advantage is you have the option of duplicating the string if you want to retain the original.
char *original = strdup(string);
trim(string);
printf("trimmed: %s\n", string);
If you don't want to modify the original string, that means you need to allocate memory for the modified string. That memory then must be freed. That means a new variable to hold the pointer so you can free it.
char *trimmed = trim(original);
printf("trimmed: %s\n", trimmed);
free(trimmed);
You can get around this by passing a function pointer into trim and having trim manage all the memory for you.
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
void trim(char *string, void(*func)(char *) )
{
// Advance the pointer to the first non-space char
while( *string == ' ' ) {
string++;
}
// Shrink the length to the last non-space char.
size_t len = strlen(string);
while(string[len-1]==' ') {
len--;
}
// Copy the string to stack memory
char trimmedStr[len + 1];
strncpy(trimmedStr,string, len);
// strncpy does not add a null byte, add it ourselves.
trimmedStr[len] = '\0';
// pass the trimmed string into the user function.
func(trimmedStr);
}
void print_string(char *str) {
printf("'%s'\n", str);
}
int main()
{
char string[]=" a sdf ie ";
trim(string, print_string);
printf("original: '%s'\n", string);
return 0;
}
Ta da! One variable, the original is left unmodified, no memory leaks.
While function pointers have their uses, this is a bit silly.
It's C. Get used to managing memory. ¯\_(ツ)_/¯
Also, is there any way where I don't have to charge original string as
well, like if I print trim(string) it will print trimmed string and
when i print only string, it will print original string
Yes you can, but you cannot allocate new memory in the trim function as you will not be holding the return memory.
You can have a static char buffer in the trim function and operate on it.
Updated version of #elcuco answer.
#include <stdio.h>
char *trim(char *string)
{
static char buff[some max length];
// trim prefix
while ((*string) == ' ' ) {
string++;
}
// find end of original string
int i = 0;
while (*string) {
buff[i++] = *string;
string++;
}
// trim suffix
while ((buff[i]) == ' ' ) {
buff[i] = '\0';
i--;
}
return buff;
}
int main()
{
char string1[] = " abcdefg abcdf ";
char *string = trim(string1);
printf("String is [%s]\n",string);
return 0;
}
With this you don't need to worry about holding reference to trim function return.
Note: Previous values of buff will be overwritten with new call to trim function.
If you don't want to change the original, then you will need to make a copy, or pass a second array of sufficient size as a parameter to your function for filling. Otherwise a simple in-place trmming is fine -- so long as the original string is mutable.
An easy way to approach trimming on leading and trailing whitespace is to determine the number of leading whitespace characters to remove. Then simply use memmove to move from the first non-whitespace character back to the beginning of the string (don't forget to move the nul-character with the right portion of the string).
That leaves only removing trailing whitespace. An easy approach there is to loop from the end of the string toward the beginning, overwriting each character of trailing whitespace with a nul-character until your first non-whitespace character denoting the new end of string is found.
A simple implementation for that could be:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define DELIM " \t\n" /* whitespace constant delimiters for strspn */
/** trim leading and trailing whitespace from s, (s must be mutable) */
char *trim (char *s)
{
size_t beg = strspn (s, DELIM), /* no of chars of leading whitespace */
len = strlen (s); /* length of s */
if (beg == len) { /* string is all whitespace */
*s = 0; /* make s the empty-string */
return s;
}
memmove (s, s + beg, len - beg + 1); /* shift string to beginning */
for (int i = (int)(len - beg - 1); i >= 0; i--) { /* loop from end */
if (isspace(s[i])) /* checking if char is whitespace */
s[i] = 0; /* overwrite with nul-character */
else
break; /* otherwise - done */
}
return s; /* Return s */
}
int main (void) {
char string1[] = " a sdf ie ";
printf ("original: '%s'\n", string1);
printf ("trimmed : '%s'\n", trim(string1));
}
(note: additional intervening whitespace was added to your initial string to show that multiple intervening whitespace is left unchanged, the output is single-quoted to show the remaining text boundaries)
Example Use/Output
$ ./bin/strtrim
original: ' a sdf ie '
trimmed : 'a sdf ie'
Look things over and let me know if you have further questions.
Suppose I have a string that may look something like this:
"value" "some other value" "other value" "some value"
My goal is to remove the blanks selectively, like so:
"value""some other value""other value""some value"
such that the blanks remain only inside strings contained in quotes:
"some other value"
I have the following function:
void rmChar(char *str, char c)
{
char *src, *dest;
src = dest = str;
while(*src != '\0')
{
if (*src != c)
{
*dest = *src;
dest++;
}
src++;
}
*dest = '\0';
}
which removes all occurrences of char c in str and I though I should use some more conditional expressions to do the removal only when certain things happen.
Got any clues?
I just thought of doing this. Below is my program.
Note: This may not be an efficient program (bad time or space complexity), however it does what you are trying to do (if I understood your question right).
Note Also I have used malloc() in this code. You would not use it if you were changing the contents of the original string without using any other string. But as I understood from your question you were making a NEW string which contained the value of original string after removing the spaces.
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void rmChar(char *,char, int );
int main()
{
char string[200] = "\"This is a value\" \"and another value\" \"value2 this\"";
char c;
c = '"';
printf("%s\n",string);
int len = strlen(string);
/*Pass the address of the stringi, char c, and the length of the string*/
/*Length of the string will be required for malloc() inside function rmChar()*/
rmChar(string, c, len);
return 0;
}
void rmChar(char *str,char c, int len)
{
char *dest1, *dest2;
char *src = str;
int removeFlag = 0; /* You will remove all the spaces ' ' that come after removeFlag is odd*/
dest1 = malloc(len);
dest2 = dest1;
while(*str != '\0')
{
if(*str == c)
{
removeFlag++;
if (removeFlag %2 == 0)
{
/* This is required because every 2nd time you get a " removeFlag is increased so next if is NOT true*/
*dest2 = *str;
dest2++;
}
}
if ((removeFlag % 2) == 1)
{
*dest2 = *str;
dest2++;
}
str++;
}
*dest2 = '\0';
printf("%s\n", dest1);
/* If you want to copy the string without spaces to the original string uncomment below line*/
//strcpy(src, dest1);
free(dest1);
}
You needed one more variable to use as some kind of flag which indicated after which " you need to remove spaces. Then you would use that flag in a if() statement in some way. Here int removeFlag is the flag I have used.
The loop that iterates over the string has to keep track if it is currently looking at a character inside a quoted string or not, and then use that information to only delete when appropriate.
To keep track of that information you could use an additional variable that gets updated every time there is a ".
int quoted = 0;
while (...) {
if (*src == '"') {
// set `quoted` to 1 or 0, as appropriate
...
}
// delete only if !quoted
...
}
As you can see below I have created a little program to concatenate 2 strings using C, as you may imagine this code doesn't work, I have already corrected it myself by using Array notation instead of pointers, and it works just fine, however I'm still not sure why is it that my code fails being almost a replica of my corrected code.
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
void concatena(char *str1, char *str2){
char *strAux;
int mover;
mover = 0;
strAux = (char *)(malloc(strlen(str1) + strlen(str2)+2));
*(strAux) = '\0';
if(str1 == '\0')
*strAux = '\0';
else
while(str1 != '\0'){
*(strAux+mover++)=*(str1++);
}
if(str2 == '\0')
*strAux = '\0';
else
while(str2 != '\0'){
*(strAux+mover++)=*(str2++);
}
strAux='\0';
str1=strAux;
printf("%s", str1);
free(strAux);
}
I´m still a C beginner (And yes, I'm aware that there are libraries like string.h, I'm asking this for academic reasons) and I have been told that char pointers and arrays are the same thing, something that confuses the heck out of me.
Any help is greatly appreciated.
The first problem I see is with this section:
if(str2 == '\0')
*strAux = '\0';
Just before this code, you've filled up strAux with the string from str1.
Then, if str2 is empty, you suddenly put a null-terminator at the beginning of strAux, eliminating all the work you've done so far!
I think what you intend is:
if(*str2 == '\0')
*(strAux+mover) = '\0';
Its the same thing again after your loop for str2, you have the code:
strAux='\0';
Again, this puts a null-terminator at the start of strAux, effectively ending the newly created string before it even gets started.
Here's how I'd re-write your code:
void concatena(char *str1, char *str2){
char *strAux;
int mover = 0;
strAux = (char *)(malloc(strlen(str1) + strlen(str2)+1)); // Changed to +1, NOT +2
*(strAux) = '\0'; // Start the string as (empty)
while(*str1 != '\0'){ // Copy the first string over.
*(strAux+mover++)=*(str1++);
}
while(*str2 != '\0'){ // Copy the second string over.
*(strAux+mover++)=*(str2++);
}
*(strAux+mover)='\0'; // End the new, combined string.
printf("%s", strAux); // Show the results.
free(strAux);
}
Accepting the same constraints, here is how I would (re)write your code. Unfortunately there is a specification shortcoming: should the concatenation occur to the first string passed? Or should a new string be created? Here are both methods:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
char *concatena (const char *str1, const char *str2)
{
char *op, *newStr = (char*)malloc (strlen (str1) + strlen (str2) + 1);
if (!newStr)
{
fprintf (stderr, "concatena: error allocating\n");
return;
}
op = newStr; // set up output pointer
while (str1 && *str1) // copy first string
*op++ = *str1++;
while (str2 && *str2) // concatenate second string
*op++ = *str2++;
*op = '\000'; // add conventional NUL termination
return newStr;
}
void concatenb (char *str1, const char *str2)
{
char *op;
if (!str1)
{
fprintf (stderr, "concatenb: NULL string 1\n");
return;
}
op = &str1 [strlen (str1)]; // set output pointer at trailing NUL
while (str2 && *str2) // concatenate second string
*op++ = *str2++;
*op = '\000'; // add conventional NUL termination
}
strAux = (char *)(malloc(strlen(str1) + strlen(str2)+2));
2 is not required, just 1 is sufficient for the termination character.
*(strAux) = '\0';
This should be happening only at the end of all your computation. Not in between the concatenation i.e.,
while(*str1 != '\0'){ // This loops copies the first string
// ^ Notice that you need to dereference to check for the termination character.
*(strAux+mover++)=*(str1++);
}
while(*str2 != '\0'){ // This loop copies the second string
*(strAux+mover++)=*(str2++);
}
// Finally adding termination character
*(strAux+mover) = '\0'; // since with mover you are keeping track of locations.
The amount of errors in your code is disheartening. You should probably pick up a good C book and start over.
First off, there's a library function that you can use to concatenate strings:
const unsigned int len = strlen(str1) + strlen(str2) + 1;
char * dst = malloc(len);
strncat(dst, str1, len);
strncat(dst, str2, len);
Now, if you insist on doing it manually, you have to get pointers and dereferencing right:
char * d = dst;
while (*str1 != 0) *dst++ = *str1++;
while (*str2 != 0) *dst++ = *str2++;
*dst = 0;
// d now points to the beginning of the concatenated string
The two loops check if the current character in the input string is nonzero, and if so, then they copy that character to the current character in the output string, and then both input and output pointer are advanced. (This is all done in one wash by use of the postfix ++ operator.) Finally, the last character is set to zero to create a new null-terminator.
In the process we modified all three pointers dst, str1 and str2. The latter two came in as input function arguments by copy, so that's fine. For returning the concatenated string we made a copy of dst before the loop, which we can return in the end.
HI, I would like how to do a split of a string in c without #include
Multiple ways of doing that, which I'll just explain and not write for you as this can only be a homework (or self-enhancement exercise, so the intent is the same).
Either you split the string into multiple strings that you re-allocate into a multi-dimensional array,
or you simply cut the string on separators and add terminal '\0' where appropriate and just copy the starting address of each sub-string to an array of pointers.
The approach for the splitting is similar in both cases, but in the second one you don't need to allocate any memory (but modify the original string), while in the first one you create safe copies of each sub-string.
You were not specific on the splitting, so I don't know if you wanted to cut on substrings, a single charater, or a list of potential separators, etc...
Good luck.
find the point you would like to split it
make two buffers large enough to contain data
strcpy() or do it manually (see example)
in this code I assume you have a string str[] and would like to split it at the first comma:
for(int count = 0; str[count] != '\0'; count++) {
if(str[count] == ',')
break;
}
if(str[count] == '\0')
return 0;
char *s1 = malloc(count);
strcpy(s1, (str+count+1)); // get part after
char *s2 = malloc(strlen(str) - count); // get part before
for(int count1 = 0; count1 < count; count1++)
s2[count1] = str[count1];
got it? ;)
Assuming I have complete control of the function prototype, I'd do this (make this a single source file (no #includes) and compile, then link with the rest of the project)
If #include <stddef.h> is part of the "without #include" thing (but it shouldn't), then instead of size_t, use unsigned long in the code below
#include <stddef.h>
/* split of a string in c without #include */
/*
** `predst` destination for the prefix (before the split character)
** `postdst` destination for the postfix (after the split character)
** `src` original string to be splitted
** `ch` the character to split at
** returns the length of `predst`
**
** it is UB if
** src does not contain ch
** predst or postdst has no space for the result
*/
size_t split(char *predst, char *postdst, const char *src, char ch) {
size_t retval = 0;
while (*src != ch) {
*predst++ = *src++;
retval++;
}
*predst = 0;
src++; /* skip over ch */
while ((*postdst++ = *src++) != 0) /* void */;
return retval;
}
Example usage
char a[10], b[42];
size_t n;
n = split(b, a, "forty two", ' ');
/* n is 5; b has "forty"; a has "two" */