views:

1501

answers:

5

Let's say I have a char* str = "0123456789" and I want to cut the first and the last three letters and print just the middle, what is the simplest, and safest, way of doing it?

Now the trick: The portion to cut and the portion to print are of variable size, so I could have a very long char*, or a very small one.

+20  A: 

You can use printf():

#include <stdio.h>

int main (void)
{
  char *str = "0123456789";

  printf("%.6s\n", str + 1);
  return 0;
}

The precision in the %s conversion specifier specifies the maximum number of characters to print. You can use a variable to specify the precision at runtime as well:

#include <stdio.h>

int main (void)
{
  int length = 6;
  char *str = "0123456789";

  printf("%.*s\n", length, str + 1);
  return 0;
}

In this example, the * is used to indicate that the next argument (length) will contain the precision for the %s conversion, the corresponding argument must be an int.

Pointer arithmetic can be used to specify the starting position as I did above.

[EDIT]

One more point, if your string is shorter than your precision specifier, less characters will be printed, for example:

#include <stdio.h>

int main (void)
{
  int length = 10;
  char *str = "0123456789";

  printf("%.*s\n", length, str + 5);
  return 0;
}

Will print "56789". If you always want to print a certain number of characters, specify both a minimum field width and a precision:

  printf("%10.10s\n", str + 5);

or

  printf("%*.*s\n", length, length, str + 5);

which will print:

"     56789"

You can use the minus sign to left-justify the output in the field:

  printf("%-10.10s\n", str + 5);

Finally, the minimum field width and the precision can be different, i.e.

  printf("%8.5s\n", str);

will print at most 5 characters right-justified in an 8 character field.

Robert Gamble
+1  A: 

If you don't mind modifying the data, you could just do some pointer arithmetic. This is assuming that str is a char pointer and not an array:

char string[] = "0123456789";
char *str = string;

str += 3; // "removes" the first 3 items
str[4] = '\0'; // sets the 5th item to NULL, effectively truncating the string

printf(str); // prints "3456"
Kyle Cronin
If you can guarantee that the string is writable, then it is 'OK'. But since it is not necessary to modify the string, it is better not to.
Jonathan Leffler
And you should save the old value before zapping it, and then reinstate it afterwards.
Jonathan Leffler
A: 

I believe there is some magic you can do with printf that will only print a certain number of characters, but it's not commonly understood or used. We tried to do it at a previous job and couldn't get it to work consistently.

What I would do is save off a character, null that character in the string, print it, then save it back.

Dan Olson
See my printf example, I've used this method for a long time and it has always worked consistently for me.
Robert Gamble
The save-null-restore trick fails abysmally when the source string is a constant. Don't do it.
Jonathan Leffler
Robert: are you sure this format (eg: %.*) works with *any* C compiler?
botismarius
@botismarius: It will work with any C library that supports C89 or C99, it has been standard for 20 years, from the C89 spec:"A field width or precision, or both, may be indicated by anasterisk * instead of a digit string. In this case, an int argumentsupplies the field width or precision."
Robert Gamble
I'm glad I got modded down after I saw how simple the example was. In our case, we had trouble getting the "use string length from the previous argument" feature to work consistently across all of the platforms we needed. That's the best of my recollection.
Dan Olson
+7  A: 

Robert Gamble and Steve separately have most of the pieces. Assembled into a whole:

void print_substring(const char *str, int skip, int tail)
{
    int len = strlen(str);
    assert(skip >= 0);
    assert(tail >= 0 && tail < len);
    assert(len > skip + tail);
    printf("%.*s", len - skip - tail, str + skip);
}

Invocation for the example:

print_substring("0123456789", 1, 3);
Jonathan Leffler
A: 

Here is a clean and simple substring function I dug up from my personal library that may be useful:

char *
substr(const char *src, size_t start, size_t len)
{
  char *dest = malloc(len+1);
  if (dest) {
    memcpy(dest, src+start, len);
    dest[len] = '\0';
  }
  return dest;
}

It's probably self-explanatory but it takes a string, a starting position (starting at zero), and a length and returns a substring of the original string or a null pointer if malloc fails. The pointer returned can be free'd by the caller when the memory is no longer needed. In the spirit of C, the function doesn't validate the starting position and length provided.

Robert Gamble