[comp.lang.c] printf and variable length string format

kjb@zycad.UUCP (Kevin Buchs) (11/22/87)

I want to print out a string whose length I will not know ahead, and which
is not null terminated.  I thought the following use of the asterisk
precision delimiter would work:
	
char *a = "hello"; int i;
for (i=0; i<6; i++) printf("%*s\n", i, a);

I thought it would print:

h
he
hel
hell
hello

but I got:

hello
hello
hello
hello
hello

Does the asterisk not work with the s descriptor?  Or, am I using it wrong.
Is there another way to do this (except fwrite)?

-- 
Kevin Buchs   3500 Zycad Dr. Oakdale, MN 55109  (612)779-5548
Zycad Corp.   {rutgers,ihnp4,amdahl,umn-cs}!meccts!nis!zycad!kjb

moran@yale.UUCP (11/23/87)

In article <692@zycad.UUCP> kjb@zycad.UUCP (Kevin Buchs) writes:
>
>I want to print out a string whose length I will not know ahead, and which
>is not null terminated.  I thought the following use of the asterisk
>precision delimiter would work:
>	
>char *a = "hello"; int i;
>for (i=0; i<6; i++) printf("%*s\n", i, a);
...some examples omitted
>
>Does the asterisk not work with the s descriptor?  Or, am I using it wrong.
>Is there another way to do this (except fwrite)?

I think the reason is that the i would specify the minimum field
width, and in this case, the field would always be larger than that.
Running this will show you what is really going on here.

main() {
 static char a[2] = "h";
 int i;

 for(i=0;i<6;i++)
  printf("foo%*s string\n",i,a);
  }

it looks like ->

fooh string
fooh string
foo h string
foo  h string
foo   h string
foo    h string

So, in answer to your question: no, * sets a min. width not the actual
width. Perhaps you should look at the BSD routines which deal with
strings which are not null terminated i.e. you could copy the string
segement into one which is null terminated and then print that, but
it isn't really clear to me whether printing the pieces is important
to you.

			  
			  William L. Moran Jr.
moran@{yale.arpa, cs.yale.edu, yalecs.bitnet}  ...{ihnp4!hsi,decvax}!yale!moran

Gonna get my PhD...I'm a teenage lobotomy!
				- The Ramones

hunt@spar.SPAR.SLB.COM (Neil Hunt) (11/23/87)

In article <692@zycad.UUCP> kjb@zycad.UUCP (Kevin Buchs) writes:
>
>I want to print out a string whose length I will not know ahead, and which
>is not null terminated.  I thought the following use of the asterisk
>precision delimiter would work:
>	
>char *a = "hello"; int i;
>for (i=0; i<6; i++) printf("%*s\n", i, a);
>
>I thought it would print:
>
>h
>he
>hel
>hell
>hello
>
>but I got:
>
>hello
>hello
>hello
>hello
>hello
>
>Does the asterisk not work with the s descriptor?  Or, am I using it wrong.
>Is there another way to do this (except fwrite)?

The format "%8s" prints a string in a min field width of 8 characters.
The format "%.8s" trucactes a string to a max field width of 8.

I just tried it, and the same applies to a variable specified field:
try:

char *a = "hello"; int i;
for (i=0; i<6; i++) printf("%.*s\n", i, a);

which prints what you were expecting.

Neil/.

lvc@tut.UUCP (11/24/87)

In article <692@zycad.UUCP>, kjb@zycad.UUCP (Kevin Buchs) writes:
> 
> I want to print out a string whose length I will not know ahead, and which
> is not null terminated.  I thought the following use of the asterisk
> precision delimiter would work:
> 	
> char *a = "hello"; int i;
> for (i=0; i<6; i++) printf("%*s\n", i, a);
> 
>	...

* here is the minimum field length , use .* for the precision.  See
K&R pg 146-147.  For your example you need:

for (i=0; i<6; i++) printf("%.*s\n", i, a);

	Larry Cipriani AT&T Network Systems at
	cbosgd!osu-cis!tut!lvc Ohio State University

moore@prefect.uucp (Peter Moore) (11/24/87)

what you want is:

	printf("%.*s", i, string);

In a declaration of the form %10.5s, the 10 is the minimum width that the
string will take, being padded if necessary.  The 5 is the maximum number
of characters from the string that will be printed.


	Peter Moore

fauman@cgl.ucsf.edu (Eric Fauman%Stroud) (11/24/87)

In article <692@zycad.UUCP> kjb@zycad.UUCP (Kevin Buchs) writes:
>
>I want to print out a string whose length I will not know ahead, and which
>is not null terminated.  I thought the following use of the asterisk
>precision delimiter would work:
>	
>char *a = "hello"; int i;
>for (i=0; i<6; i++) printf("%*s\n", i, a);
>
>Does the asterisk not work with the s descriptor?  Or, am I using it wrong.


The number following the % is a width specifier, which in the case of a string >
is the minimum width to use.  To specify the precision, the number should
follow the %, an optional width field, and a period.  The following code
will produce the desired output:

char *cp; int i;
cp = "hello";
for (i=1;i<6;++i)
	printf("%.*s\n",i+1,cp);

Eric Fauman - University of California - San Francisco

ark@alice.UUCP (11/24/87)

In article <692@zycad.UUCP>, kjb@zycad.UUCP writes:
> 
> I want to print out a string whose length I will not know ahead, and which
> is not null terminated.  I thought the following use of the asterisk
> precision delimiter would work:
> 	
> char *a = "hello"; int i;
> for (i=0; i<6; i++) printf("%*s\n", i, a);
 
Almost.  You want to say

  for (i=0; i<6; i++) printf("%*.s\n", i, a);

When printing a string (or anything else), the number immediately
after the % means the "field width" and represents the MINIMUM
amount of space the field should occupy in the output.

The number after the . means different things for different format
types; for strings it represents the MAXIMUM number of characters
transferred to the output.

msb@sq.UUCP (11/27/87)

There have been several followups to this one, but they have all missed
a significant point.

> I want to print out a string whose length I will not know ahead, and which
> is not null terminated.
> [I tried] ... printf("%*s\n", i, a);

Most people pointed out that he meant "%.*s".  However, according to the
ANSI Draft Standard, that usage is NOT portable.  The reason is that the
Draft requires that the argument a "shall be a pointer to a string".
And a string is defined as "an array of characters *terminated by a null
character*" [my emphasis].

This means that an implementation may, on encountering the above, cheerfully
go on reading characters past the end of a until it runs off the end of
memory (or the segment of memory) and aborts... no matter what the value
of i is.

Fully portable ways to do this would be:

[1]		{ int j; for (j = 0; j < i; ++j) putchar (a[j]); }
		putchar ('\n');

[2]		{ char *tmp = malloc (j+1); if (!tmp) abort();
		  strncpy (tmp, a, j); tmp[j] = 0;
		  printf ("%s\n", tmp); free (tmp); }

If you know a maximum size for a, you can avoid the malloc().

If a is known not to be const, there is also:

[3]		if (j) { char last = a[j-1]; a[j-1] = '\0';
			 printf ("%s%c", a, last); a[j-1] = last; }
		putchar ('\n');

It seems to me that both [1] and [2] are likely to be significantly slower
than the "%.*s" method, which I expect will work on most or all existing
implementations; and [3] is ugly and not always usable.  I can't think
of another reasonable approach, anyway.  (You can't use fwrite(), for
instance, because the Draft allows an implementation to distinguish text
streams from binary streams.)


Doug Gwyn pointed out the "string" requirement to me when I asked him by
email about the acceptability of printf ("%.1s", &char_variable) to print
a char unless it was a null.  I agreed that this was a minor convenience
at best.  But thinking about the above example, and what you'd have to do,
does make me wonder whether the Committee really considered this point
before deciding to restrict all forms of %s to "strings".  They might have
decided that C was so strongly oriented to null-terminated strings that
anyone who wants to use data structures like a is doing so at their own
risk; or it might just have slipped by.  You there, Doug?


Mark Brader, SoftQuad Inc., Toronto	      "Suspicion breeds confidence."
utzoo!sq!msb, msb@sq.com					  -- BRAZIL

gwyn@brl-smoke.ARPA (Doug Gwyn ) (11/28/87)

In article <1987Nov27.024339.12253@sq.uucp> msb@sq.UUCP (Mark Brader) writes:
-...  But thinking about the above example, and what you'd have to do,
-does make me wonder whether the Committee really considered this point
-before deciding to restrict all forms of %s to "strings".  They might have
-decided that C was so strongly oriented to null-terminated strings that
-anyone who wants to use data structures like a is doing so at their own
-risk; or it might just have slipped by.  You there, Doug?

Hi.  I think the idea was that X3J11 wanted to allow the implementation
to be able to snarf up the argument with strcpy() etc. and this required
insisting that it be a properly-formed string.  However, I might misremember.

msb@sq.UUCP (11/29/87)

To my question of why X3J11 did require that a %s-with-precision ("%.2s")
only be guaranteed to work with null-terminated strings, Doug Gwyn said:

> I think the idea was that X3J11 wanted to allow the implementation
> to be able to snarf up the argument with strcpy() etc.

I hope that's not all it was.  "etc." says it.  Use strncpy()!

There might also be case for removing the restriction on the grounds
of conformance with existing practice, depending on the behavior of
the widely used implementations.  I'm sure I'm not the only one who's
assumed in good faith that the null was not needed.

I realize it's quite late, but the more I think about it the more I
think that the restriction is unreasonable and should be lifted.
What chance?

Mark Brader		"The last 10% of the performance sought contributes
Toronto			 one-third of the cost and two-thirds of the problems."
utzoo!sq!msb, msb@sq.com				-- Norm Augustine

gwyn@brl-smoke.ARPA (Doug Gwyn ) (12/01/87)

In article <1987Nov29.082912.2800@sq.uucp> msb@sq.UUCP (Mark Brader) writes:
>I realize it's quite late, but the more I think about it the more I
>think that the restriction is unreasonable and should be lifted.
>What chance?

Presumably if you get your comments into X3 or Tom Plum this week,
flagged as an "unsatisfactory response" received on one of your
formal review comments, X3J11 will be obliged to consider them at
the meeting next week.  The plan is for this to be the last meeting
at which substantive changes to the draft proposed ANS will be made,
but nobody knows whether things will happen that way.  So long as
we keep making changes, publication of the C standard keeps getting
delayed.  A lot of people really want to get the standard out soon.