views:

855

answers:

3

I have a C string that looks like "Nmy stringP", where N and P can be any character. How can I edit it into "my string" in C?

+6  A: 

To "remove" the 1st character point to the second character:

char mystr[] = "Nmy stringP";
char *p = mystr;
p++; /* 'N' is not in `p` */

To remove the last character replace it with a '\0'

p[strlen(p)-1] = 0; /* 'P' is not in `p` (and it isn't in `mystr` either) */
pmg
This may not work because by initializing "Nmy stringP" as a literal, the compiler/linker can place it in read only memory, causing the edit to fail.
Suppressingfire
@Suppressingfire: "Nmy stringP" is indeed a literal string, but it is copied (char by char) to the array `mystr`. The array `mystr` is modifiable.
pmg
@pmg: Where are you copying? `char *p = mystr` points `p` to `mystr`, it doesn't copy `mystr`.
quark
@pmg: May I recommend `strdup`?
quark
The literal string is copied to the location of the variable named "mystr". mystr is _not_ a pointer to the literal, it's a copy of the literal.
Bryan Oakley
@Bryan Oakley: `mystr` points to the literal array, but that array can be in read-only memory, no?
quark
`strdup()` is not a Standard function. Its workings, on those implementations that offer it as an extension, may be different from implementation to implementation. I prefer to `malloc()` and `strcpy()` when needed. It wasn't needed to demonstrate how to remove the first and last chars of a string.
pmg
mystr is not a pointer, it is an array initialized with the contents of a string literal. The code he has is correct.
Brian R. Bondy
char sz[] = "hello";You will have sizeof(sz) == 6, NOT sizeof(sz) == 4.
Brian R. Bondy
@pmd: Re: `strdup` vs. `malloc` and `strcpy`: noted.
quark
@pmd and others: What was confusing me is that pmd uses "copy". What I assume you mean is that the string is laid out on the stack, not in static (possibly read-only) memory. I had forgotten that.
quark
@quark: doing `char arr[] = "hi";` is the same as `char arr[3]; arr[0] = 'h'; arr[1] = 'i'; arr[2] = '\0';`
pmg
@Brian R. Bondy: No mystr is not a pointer but it refers to the start of the array anyway. The issue isn't the size of the array (which the compiler can tell you, regardless of where the array was allocated), it was what kind of memory it was allocated it.
quark
@pmd: Right. I'm used to making string literals `const` and making them global, rather than making them local. Local arrays go on the stack, and I'd forgotten that. Sorry for the quibbling.
quark
It doesn't matter if it's global or not. `char arr[] = "foo";` copies the elements of the string literal (char by char: 'f', 'o', 'o', and '\0') to the array `arr`. It matters when you have pointers: `char *arr = "foo";` no copying here, whether it's a global or not.
pmg
Just thought I'd throw `char arr[] = { 'f', 'o', 'o', '\0' };` into the mix for good measure, kinda solidifies the initialization part. Thanks.
James Morris
A: 

The most efficient way:

//Note destroys the original string by removing it's last char
// Do not pass in a string literal.
char * getAllButFirstAndLast(char *input)
{
  int len = strlen(input); 
  if(len > 0)
    input++;//Go past the first char
  if(len > 1)
    input[len - 2] = '\0';//Replace the last char with a null termination
  return input;
}


//...
//Call it like so
char str[512];
strcpy(str, "hello world");
char *pMod = getAllButFirstAndLast(str);

The safest way:

void getAllButFirstAndLast(const char *input, char *output)
{
  int len = strlen(input);
  if(len > 0)
    strcpy(output, ++input);
  if(len > 1)
    output[len - 2] = '\0';
}


//...
//Call it like so
char mod[512];
getAllButFirstAndLast("hello world", mod);

The second way is less efficient but it is safer because you can pass in string literals into input. You could also use strdup for the second way if you didn't want to implement it yourself.

Brian R. Bondy
The problem I see with your second implementation is that you need to make sure your output is large enough to handle the copy before you call getAllBufFirstAndLast(). In this case, I would recommend adding an outputLength variable to the call and let the called function manipulate output with strn* functions.
shank
yes that's a precondition, yup you can add a length parameter.
Brian R. Bondy
+2  A: 

Another option, again assuming that "edit" means you want to modify in place:

void topntail(char *str) {
    size_t len = strlen(str);
    assert(len >= 2); // or whatever you want to do with short strings
    memmove(str, str+1, len-2);
    str[len-2] = 0;
}

This modifies the string in place, without generating a new address as pmg's solution does. Not that there's anything wrong with pmg's answer, but in some cases it's not what you want.

Steve Jessop