somebody please explain about strings in c? - c

how to store two strings one after other without concatenation (we can increment the address)
char str[10];
scanf("%s",str);
str=str+9;
scanf("%s",str);
NOTE: Here if I give first string as BALA and 2nd as HI, it should print as HI after BALA . But HI should not replace BALA.

You cannot increment (or change in any other way) an array like that, the array variable (str) is a constant which cannot be changed.
You can do it like so:
char str[64];
scanf("%s", str);
scanf("%s", str + strlen(str));
This will first scan into str, then immediately scan once more, starting the new string right on top of the terminating '\0' of the first string.
If you enter "BALA" first, the beginning of str will look like this:
+---+---+---+---+----+
str: | B | A | L | A | \0 |
+---+---+---+---+----+
and since strlen("BALA") is four, the next string will be scanned into the buffer starting right on top of the '\0' visible above. If you then enter "HI", str will start like so:
+---+---+---+---+---+---+----+
str: | B | A | L | A | H | I | \0 |
+---+---+---+---+---+---+----+
At this point, if you print str it will print as "BALAHI".
Of course, this is very dangerous and likely to introduce buffer overrun, but that's what you wanted.

If I understand what you want to do correctly perhaps you want to put the strings in an array.
So a modified version of your code would look something like
char strings[ARRAY_LENGTH][MAX_STRING_LENGTH];
char* str = strings[0];
scanf("%s",str);
str=strings[1];
scanf("%s",str);
Then to print all the strings you would have to loop over the array like this
int i;
for(i = 0; i < ARRAY_LENGTH; i++)
{
printf(strings[i]);
}
(you would have to define ARRAY_LENGTH and MAX_STRING_LENGTH)

Moving in a similar direction to unwind, you can use the %n directive to determine how many bytes have been read. Don't forget to subtract any leading whitespace. You may also want to read your manual regarding scanf very carefully, but paying particular care to the "RETURN VALUE" section. Handling the return value is necessary to ensure a string was actually read and avoid undefined behaviour.
char str[64];
int whitespace_length, str_length, total_length;
/* NOTE: Don't handle errors with assert!
* You should replace this with proper error handling */
assert(scanf(" %n%s%n", &whitespace_length, str, &total_length) == 1);
str_length = total_length - str_length;
assert(scanf(" %n%s%n", &whitespace_length, str + str_length, &total_length) == 1);
str_length += total_length - str_length;

May be you are looking at some thing like this
char arr[100] = {0,}, *str = NULL;
/** initial String will be scanned from beginning **/
str = arr;
/** scan first string **/
fgets(str, 100, stdin);
/** We need to replace NULL termination with space is space is delimiter **/
str += strlen(str)-1;
*str = ' ' ;
/** scan second string from address just after space,
we can not over ride the memory though **/
fgets(str, 100 - strlen(str), stdin);
printf("%s",arr);
Well incase you need the same with scanf
char arr[100] = {0,}, *str = NULL;
/** initial String will be scanned from beginning **/
str = arr;
/** scan first string **/
scanf("%s",str);
/** We need to replace NULL termination with space is space is delimiter **/
str += strlen(str);
*str = ' ' ;
/** scan second string from address just after space,
* we can not over ride the memory though **/
str++;
scanf("%s",str);
printf("%s",arr);

Related

Can't enter 2 inputs from user with unknown length of char pointer

I am trying to get 2 inputs from user with unknown length of char pointer.(Working on dynamically allocating memory) But after entering 1st input with a "space" it doesn't wait for user to enter second input, it only reads one word and then it allows second input.
char *str1;
char ch;
printf("Enter a sentence:(Ex: Computer Engineer)");
str1 = (char*)malloc(sizeof(char*));
scanf(" %s", str1);
printf("Enter a character to search(Ex: g):");
scanf(" %c", &ch);
char *result;
result=mystrchr(str1,ch);
if(result!=NULL)
printf("%s",result);
else
printf("NULL");
The second exact same statement of
str1 = (char*) malloc(sizeof(char*));
is redundant, inappropriate and useless. By doing so you allocate another memory space to which str1 is pointing to; leaving the previous allocated space because of no free()ing abandoned but existing in memory.
char *str1;
str1 = (char*) malloc(sizeof(char*));
With that call to malloc() you allocate memory of the size of a pointer to char, usually 4 bytes on most modern systems, not the space needed to store a string like "Computer Engineer". With this definition it is only be able to store a string of 3 characters plus the automatically appended string-terminating null character (4 characters for 4 bytes).
By putting in a string longer than 3 characters by the call to scanf():
scanf(" %s", str1);
the program will write beyond the bounds of the allocated memory, which invokes undefined behavior (like it was high-probably happened in your case).
You need to allocate enough space to hold the provided strings - in the case of f.e. "Computer Engineer" it needs to have at least 18 byte (Note that sizeof(char) == 1):
char *str1 = malloc((sizeof(char) * 18);
or alternatively:
char *str1 = malloc((sizeof(*str1) * 18);
Note that you can´t put in a white space separated word by using the %s format specifier. For achieve that, use %[ instead:
scanf("%[^\n]", str1);
Or even better use the more reliable and safer fgets():
fgets(str1,18,stdin);
If you want to allocate memory dependent upon the input of a user, you need to add another input request and variable before allocating:
int len;
printf("How many characters the string should have?");
scanf("%d",&len);
char *str1 = malloc((sizeof(*str1) * (len + 1)); // +1 element for null character.
printf("Enter a sentence:(Ex: Computer Engineer)");
fgets(str1,len,stdin);
Side note:
You do not need to cast the returned pointer of malloc -> Do I cast the result of malloc?
Programming is not just aligning lines of codes. Memory allocation in C is not very complex but it is very easy to write erroneous code with it (you did...).
This is just err... bad code:
str1 = (char*)malloc(sizeof(char*)); // Wrong: you allocate a char array with
// the size of a char pointer: nonsense
printf("Enter a sentence:(Ex: Computer Engineer)"); // no error here :-)
str1 = (char*)malloc(sizeof(char*)); // Wrong: the previously allocated array is
// now leaked: you can no longer free it
scanf(" %s", str1); // What if input is larger than sizeof(char*)?
Now for your question: scanf reads blank delimited tokens. It is a feature. If you want line oriented input, you should use fgets.
Code could be:
#define SIZE 16
size_t size = SIZE; // initial size of the allocated array
char *str1 = malloc(size); // sizeof(char) is by definition 1
char ch[2]; // a trick to read next non blank character
printf("Enter a sentence:(Ex: Computer Engineer)");
char *str = str1;
for(;;) { // loop to accept arbitrary length input
if (NULL == fgets(str, size, stdin)) {
perror("Input error or end of file");
return 1;
}
if (strchr(str, '\n') != -1) break; // ok we have a full line
str1 = realloc(str1, size * 2);
if (NULL == str1) {
perror("Could not allocate memory");
return 1;
str = str1 + size - 1; // point after currently got data
size = size * 2;
}
printf("Enter a character to search(Ex: g):");
scanf("%1s", ch);
char *result = mystrchr(str1,ch[0]);
...
Beware: code is untested and could contain typos...

Scanning data from text file, that doesn't have spacing between each item of data

I have encountered a problem with my homework. I need to scan some data from a text file, to a struct.
The text file looks like this.
012345678;danny;cohen;22;M;danny1993;123;1,2,4,8;Nice person
223325222;or;dan;25;M;ordan10;1234;3,5,6,7;Singer and dancer
203484758;shani;israel;25;F;shaninush;12345;4,5,6,7;Happy and cool girl
349950234;nadav;cohen;50;M;nd50;nadav;3,6,7,8;Engineer very smart
345656974;oshrit;hasson;30;F;osh321;111;3,4,5,7;Layer and a painter
Each item of data to its matching variable.
id = 012345678
first_name = danny
etc...
Now I can't use fscanf because there is no spacing, and the fgets scanning all the line.
I found some solution with %[^;]s, but then I will need to write one block of code and, copy and past it 9 times for each item of data.
Is there any other option without changing the text file, that similar to the code I would write with fscanf, if there was spacing between each item of data?
************* UPDATE **************
Hey, First of all, thanks everyone for the help really appreciating.
I didn't understand all your answers, but here something I did use.
Here's my code :
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
char *idP, *firstNameP, *lastNameP;
int age;
char gender, *userNameP, *passwordP, hobbies, *descriptionP;
}user;
void main() {
FILE *fileP;
user temp;
char test[99];
temp.idP = (char *)malloc(99);
temp.firstNameP = (char *)malloc(99);
temp.lastNameP = (char *)malloc(99);
temp.age = (int )malloc(4);
temp.gender = (char )malloc(sizeof(char));
temp.userNameP = (char *)malloc(99);
fileP = fopen("input.txt", "r");
fscanf(fileP, "%9[^;];%99[^;];%99[^;];%d;%c", temp.idP,temp.firstNameP,temp.lastNameP,&temp.age, temp.gender);
printf("%s\n%s\n%s\n%d\n%c", temp.idP, temp.firstNameP, temp.lastNameP, temp.age, temp.gender);
fgets(test, 60, fileP); // Just testing where it stop scanning
printf("\n\n%s", test);
fclose(fileP);
getchar();
}
It all works well until I scan the int variable, right after that it doesn't scan anything, and I get an error.
Thanks a lot.
As discussed in the comments, fscanf is probably the shortest option (although fgets followed by strtok, and manual parsing are viable options).
You need to use the %[^;] specifier for the string fields (meaning: a string of characters other than ;), with the fields separated by ; to consume the actual semicolons (which we specifically requested not to be consumed as part of the string field). The last field should be %[^\n] to consume up to the newline, since the input doesn't have a terminating semicolon.
You should also (always) limit the length of each string field read with a scanf family function to one less than the available space (the terminating NUL byte is the +1). So, for example, if the first field is at most 9 characters long, you would need char field1[10] and the format would be %9[^;].
It is usually a good idea to put a single space in the beginning of the format string to consume any whitespace (such as the previous newline).
And, of course you should check the return value of fscanf, e.g., if you have 9 fields as per the example, it should return 9.
So, the end result would be something like:
if (fscanf(file, " %9[^;];%99[^;];%99[^;];%d;%c;%99[^;];%d;%99[^;];%99[^\n]",
s.field1, s.field2, s.field3, &s.field4, …, s.field9) != 9) {
// error
break;
}
(Alternatively, the field with numbers separated by commas could be read as four separate fields as %d,%d,%d,%d, in which case the count would go up to 12.)
Here you have simple tokenizer. As I see you have more than one delimiter here (; & ,)
str - string to be tokenized
del - string containing delimiters (in your case ";," or ";" only)
allowempty - if true allows empty tokens if there are two or more consecutive delimiters
return value is a NULL terminated table of pointers to the tokens.
char **mystrtok(const char *str, const char *del, int allowempty)
{
char **result = NULL;
const char *end = str;
size_t size = 0;
int extrachar;
while(*end)
{
if((extrachar = !!strchr(del, *end)) || !*(end + 1))
{
/* add temp variable and malloc / realloc checks */
/* free allocated memory on error */
if(!(!allowempty && !(end - str)))
{
extrachar = !extrachar * !*(end + 1);
result = realloc(result, (++size + 1) * sizeof(*result));
result[size] = NULL;
result[size -1] = malloc(end - str + 1 + extrachar);
strncpy(result[size -1], str, end - str + extrachar);
result[size -1][end - str + extrachar] = 0;
}
str = end + 1;
}
end++;
}
return result;
}
To free the the memory allocated by the tokenizer:
void myfree(char **ptr)
{
char **savedptr = ptr;
while(*ptr)
{
free(*ptr++);
}
free(savedptr);
}
Function is simple but your can use any separators and any number of separators.

Split a string based on contiguous delimiters

I'm looking to split a sting based on a specific sequence of characters but only if they are in order.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int i = 0;
char **split;
char *tmp;
split = malloc(20 * sizeof(char *));
tmp = malloc(20 * 12 * sizeof(char));
for(i=0;i<20;i++)
{
split[i] = &tmp[12*i];
}
char *line;
line = malloc(50 * sizeof(char));
strcpy(line, "Test - Number -> <10.0>");
printf("%s\n", line);
i = 0;
while( (split[i] = strsep(&line, " ->")) != NULL)
{
printf("%s\n", split[i]);
i++;
}
}
This will print out:
Test
Number
<10.0
However I just want to split around the -> so it could give the output:
Test - Number
<10.0>
I think the best way to do the splits with an ordered sequence of delimeters is
to replicate strtok_r behaviour using strstr, like this:
#include <stdio.h>
#include <string.h>
char *substrtok_r(char *str, const char *substrdelim, char **saveptr)
{
char *haystack;
if(str)
haystack = str;
else
haystack = *saveptr;
char *found = strstr(haystack, substrdelim);
if(found == NULL)
{
*saveptr = haystack + strlen(haystack);
return *haystack ? haystack : NULL;
}
*found = 0;
*saveptr = found + strlen(substrdelim);
return haystack;
}
int main(void)
{
char line[] = "a -> b -> c -> d; Test - Number -> <10.0> ->No->split->here";
char *input = line;
char *token;
char *save;
while(token = substrtok_r(input, " ->", &save))
{
input = NULL;
printf("token: '%s'\n", token);
}
return 0;
}
This behaves like strtok_r but only splits when the substring is found. The
output of this is:
$ ./a
token: 'a'
token: ' b'
token: ' c'
token: ' d; Test - Number'
token: ' <10.0>'
token: 'No->split->here'
And like strtok and strtok_r, it requires that the source string is
modifiable, as it writes the '\0'-terminating byte for creating and returning
the tokens.
EDIT
Hi, would you mind explaining why '*found = 0' means the return value is only the string in-between delimiters. I don't really understand what is going on here or why it works. Thanks
The first thing you've got to understand is how strings work in C. A string is
just a sequence of bytes (characters) that ends with the '\0'-terminating
byte. I wrote bytes and characters in parenthesis, because a character in C is
just a 1-byte value (on most systems a byte is 8 bit long) and the integer
values representing the characters are those defined in the ASSCI code
table, which are 7-bit long values. As you can see from the table the
value 97 represents the character 'a', 98 represents 'b', etc. Writing
char x = 'a';
is the same as doing
char x = 97;
The value 0 is an special value for strings, it is called NUL (null character)
or '\0'-terminating byte. This value is used to tell the functions where a
string ends. A function like strlen that returns the length of a string, does
it by counting how many bytes it encounters until it encounters a byte with
the value 0.
That's why strings are stored using char arrays, because a pointer to an array
gives to the start of the memory block where the sequence of chars is stored.
Let's look at this:
char string[] = { 'H', 'e', 'l', 'l', 'o', 0, 48, 49, 50, 0 };
The memory layout for this array would be
0 1 2 3 4 5 6 7 8 9
+-----+-----+-----+-----+-----+----+-----+-----+-----+----+
| 'H' | 'e' | 'l' | 'l' | 'o' | \0 | '0' | '1' | '2' | \0 |
+-----+-----+-----+-----+-----+----+-----+-----+-----+----+
or to be more precise with the integer values
0 1 2 3 4 5 6 7 8 9 10
+----+-----+-----+-----+-----+---+----+----+----+---+
| 72 | 101 | 108 | 108 | 111 | 0 | 48 | 49 | 50 | 0 |
+----+-----+-----+-----+-----+---+----+----+----+---+
Note that the value 0 represents '\0', 48 represents '0', 49 represents
'1' and 50 represents '2'. If you do
printf("%lu\n", strlen(string));
the output will be 5. strlen will find the value 0 at the 5th position and
stop counting, however string stores two strings, because from the 6th
position on, a new sequence of characters starts that also terminates with 0, thus making it a
second valid string in the array. To access it, you would need to have pointer
that points past the first 0 value.
printf("1. %s\n", string);
printf("2. %s\n", string + strlen(string) + 1);
The output would be
Hello
012
This property is used in functions like strtok (and mine above) to return you
a substring from a larger string, without the need of creating a copy (that would be
creating a new array, dynamically allocating memory, using strcpy to create
the copy).
Assume you have this string:
char line[] = "This is a sentence;This is another one";
Here you have one string only, because the '\0'-terminating byte comes after
the last 'e' in the string. If I however do:
line[18] = 0; // same as line[18] = '\0';
then I created two strings in the same array:
"This is a sentence\0This is another one"
because I replaced the semicolon ';' with '\0', thus creating a new string
from position 0 to 18 and a second one from position 19 to 38. If I do now
printf("string: %s\n", line);
the output will be
string: This is a sentence
Now let's us take look at the function itself:
char *substrtok_r(char *str, const char *substrdelim, char **saveptr);
The first argument is the source string, the second argument is the delimiters
strings and the third one is doule pointer of char. You have to pass a pointer
to a pointer of char. This will be used to remember where the function should
resume scanning next, more on that later.
This is the algorithm:
if str is not NULL:
start a new scan sequence from str
otherwise
resume scanning from string pointed to by *saveptr
found position of substring_d pointed to by 'substrdelim'
if no such substring_d is found
if the current character of the scanned text is \0
no more substrings to return --> return NULL
otherwise
return the scanned text and set *saveptr to
point to the \0 character of the scanned text,
so that the next iteration ends the scanning
by returning NULL
otherwise (a substring_d was found)
create a new substring_a until the found one
by setting the first character of the found
substring_d to 0.
update *saveptr to the start of the found substring_d
plus it's previous length so that *saveptr
points to the past the delimiter sequence found in substring_d.
return new created substring_a
This first part is easy to understand:
if(str)
haystack = str;
else
haystack = *saveptr;
Here if str is not NULL, you want to start a new scan sequence. That's why
in main the input pointer is set to point to the start of the string saved
in line. Every other iteration must be called with str == NULL, that's
why the first thing is done in the while loop is to set input = NULL; so
that substrtok_r resumes scanning using *saveptr. This is the standard
behaviour of strtok.
The next step is to look for a delimiting substring:
char *found = strstr(haystack, substrdelim);
The next part handles the case where no delimiting substring is
found2:
if(found == NULL)
{
*saveptr = haystack + strlen(haystack);
return *haystack ? haystack : NULL;
}
*saveptr is updated to point past the whole source, so that it points to the
'\0'-terminating byte. The return line can be rewritten as
if(*haystack == '\0')
return NULL
else
return haystack;
which says if the source already is an empy string1, then return
NULL. This means no more substring are found, end calling the function. This
is also standard behaviour of strtok.
The last part
*found = 0;
*saveptr = found + strlen(substrdelim);
return haystack;
is handles the case when a delimiting substring is found. Here
*found = 0;
is basically doing
found[0] = '\0';
which creates substrings as explained above. To make it clear once again, before
Before
*found = 0;
*saveptr = found + strlen(substrdelim);
return haystack;
the memory looks like this:
+-----+-----+-----+-----+-----+-----+
| 'a' | ' ' | '-' | '>' | ' ' | 'b' | ...
+-----+-----+-----+-----+-----+-----+
^ ^
| |
haystack found
*saveptr
After
*found = 0;
*saveptr = found + strlen(substrdelim);
the memory looks like this:
+-----+------+-----+-----+-----+-----+
| 'a' | '\0' | '-' | '>' | ' ' | 'b' | ...
+-----+------+-----+-----+-----+-----+
^ ^ ^
| | |
haystack found *saveptr
because strlen(substrdelim)
is 3
Remember if I do printf("%s\n", haystack); at this point, because the '-' in
found has been set to 0, it will print a. *found = 0 created two strings out
of one like exaplained above. strtok (and my function which is based on
strtok) uses the same technique. So when the function does
return haystack;
the first string in token will be the token before the split. Eventually
substrtok_r returns NULL and the loop exists, because substrtok_r returns
NULL when no more split can be created, just like strtok.
Fotenotes
1An empty string is a string where the first character is already the
'\0'-terminating byte.
2This is very important part. Most of the standard functions in the C
library like strstr will not return you a new string in memory, will
not create a copy and return a copy (unless the documentation says so). The
will return you a pointer pointing to the original plus an offset.
On success strstr will return you a pointer to the start of the substring,
this pointer will be at an offset to the source.
const char *txt = "abcdef";
char *p = strstr(txt, "cd");
Here strstr will return a pointer to the start of the substring "cd" in
"abcdef". To get the offset you do p - txt which returns how many bytes
there are appart
b = base address where txt is pointing to
b b+1 b+2 b+3 b+4 b+5 b+6
+-----+-----+-----+-----+-----+-----+------+
| 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | '\0' |
+-----+-----+-----+-----+-----+-----+------+
^ ^
| |
txt p
So txt points to address b, p points to address b+2. That's why you get
the offset by doing p-txt which would be (b+2) - b => 2. So p points to
the original address plus the offset of 2 bytes. Because of this bahaviour
things like *found = 0; work in the first place.
Note that doing things like txt + 2 will return you a new pointer pointing to
the where txt points plus the offset of 2. This is called pointer arithmetic.
It's like regualr arithmetic but here the compiler takes the size of an object
into consideration. char is a type that is defined to have the size of 1,
hence sizeof(char) returns 1. But let's say you have an array of integers:
int arr[] = { 7, 2, 1, 5 };
On my system an int has size of 4, so an int object needs 4 bytes in memory.
This array looks like this in memory:
b = base address where arr is stored
address base base + 4 base + 8 base + 12
in bytes +-----------+-----------+-----------+-----------+
| 7 | 2 | 1 | 5 |
+-----------+-----------+-----------+-----------+
pointer arr arr + 1 arr + 2 arr + 3
arithmetic
Here arr + 1 returns you a pointer pointing to where arr is stored plus an
offset of 4 bytes.

What is going on in this simple C code?

I am in the process of teaching myself C. I have the following code that prints a string char by char forwards and backwards:
#include<stdio.h>
#include<string.h>
main(){
char *str;
fgets(str, 100, stdin);
//printf("%i", strlen(str));
int i;
for(i = 0; i < strlen(str) - 1; i++){
printf("%c", str[i]);
}
for(i = strlen(str); i > -1; i--){
printf("%c", str[i]);
}
}
When run, it gives me the following output (assuming I typed "hello"):
cello
ollec
In addition, if I uncomment the 7th line of code, I get the following output (assuming I typed "hello"):
6 ♠
For the life of me, I cannot figure out what I am doing that is causing the first character in the output to change. In the second example, I know that the string length would be 6 because 'h' + 'e' + 'l' + 'l' + 'o' + '\0' = 6. That is fine, but where is the spade symbol coming from? Why is it only printing one of them?
It is pretty obvious to me that I have some kind of fundamental misunderstanding of what is happening under the hood here and I cant find any examples of this elsewhere. Can anyone explain what is going wrong here?
You never allocate memory for the string. Instead of
char *str;
use
char str[100];
so that you have enough space for the up to 100 characters you read in there with the fgets call.
In this code:
char *str;
fgets(str, 100, stdin);
str points to an effectively random location. Then you tell fgets to read characters and put them where str is pointing. This causes undefined behaviour; the symptoms you are seeing probably occur because str happened to point to some memory where the first character of that memory that was being used for other purposes, but the other characters weren't being used.
Instead you need to allocate memory:
char str[100]; // allocate 100 bytes
fgets(str, 100, stdin);
Pointers only point at memory which already is allocated somewhere; they do not "own" or "contain" any memory.
You should preallocate space for your string, otherwise you are writing to who knows where, which is bad.
char str[100]; //I must be big enough to hold anything fgets might pass me
You should also be sure to only access parts of the string which contain characters:
for(i = strlen(str)-1; i > -1; i--){
printf("%c", str[i]);
}
Note that the character at strlen(str) is \0, the string-terminating null character. So you can access this space, but trying to print it or otherwise treating it like a standard letter is going to lead to issues at some point.
Your str is a pointer to char, but you don't have any actual character buffer for it to point to. You need a character array instead:
char str[100];
Only then can fgets have somewhere to store the data it reads.
Then on your reverse-printing loop, your indices are wrong:
for(i = strlen(str); i > -1; i--){
With the above, you try to print str[i] for i = strlen(str), but that's one past the end of the valid string data. Change to:
for(i = strlen(str) - 1; i > -1; i--){
The issue is that you are not allocating your
char *str
what you need to do is either
1)
char *str = malloc(sizeof(char) * 100);
and then when you are no longer using it:
free(str)
2)
char str[100];

The simplest way of printing a portion of a char[] in C

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.
You can use printf(), and a special format string:
char *str = "0123456789";
printf("%.6s\n", str + 1);
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:
int length = 6;
char *str = "0123456789";
printf("%.*s\n", length, str + 1);
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:
int length = 10;
char *str = "0123456789";
printf("%.*s\n", length, str + 5);
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 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);
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"
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.
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.

Resources