www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Using writef to print strings that contain format specifiers

reply Kramer <Kramer_member pathlink.com> writes:
While printing file names on my system using writef, I came across this file
name: 311abc%20-%20tmb[1] (percent signs included) and received this error:
311abcError: std.format.

I'm guessing writef got tripped up because the % sign is used as part of a
format string for writef.  I tried taking the file name and putting single,
double and the wysiwyg identifier "r" in front of it and tried to print it with
writef, but I still get the error.  Is there any way around this?  I wouldn't
have been able to anticipate thie file name on my system, so, maybe writef is
not a good candidate for what I'm doing?

This will reproduce the error:
# import std.stdio;
#
# void main()
# {
#     writef("311abc%20-%20tmb[1]");
# }

-Kramer
Jan 29 2005
next sibling parent reply =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Kramer wrote:

 I'm guessing writef got tripped up because the % sign is used as part of a
 format string for writef.  I tried taking the file name and putting single,
 double and the wysiwyg identifier "r" in front of it and tried to print it with
 writef, but I still get the error.  Is there any way around this?  I wouldn't
 have been able to anticipate thie file name on my system, so, maybe writef is
 not a good candidate for what I'm doing?

The first argument to writef is *not* a string, but a format specifier. Thus, you need to use this instead: writefln("%s","311abc%20-%20tmb[1]") Works the same way as with printf (in C). --anders
Jan 29 2005
next sibling parent Kramer <Kramer_member pathlink.com> writes:
D'oh!

Thanks!

-Kramer

In article <cth8jr$ni2$1 digitaldaemon.com>,
=?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= says...
Kramer wrote:

 I'm guessing writef got tripped up because the % sign is used as part of a
 format string for writef.  I tried taking the file name and putting single,
 double and the wysiwyg identifier "r" in front of it and tried to print it with
 writef, but I still get the error.  Is there any way around this?  I wouldn't
 have been able to anticipate thie file name on my system, so, maybe writef is
 not a good candidate for what I'm doing?

The first argument to writef is *not* a string, but a format specifier. Thus, you need to use this instead: writefln("%s","311abc%20-%20tmb[1]") Works the same way as with printf (in C). --anders

Jan 29 2005
prev sibling next sibling parent reply Nick <Nick_member pathlink.com> writes:
In article <cth8jr$ni2$1 digitaldaemon.com>,
=?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= says...
The first argument to writef is *not* a string, but a format specifier.

Thus, you need to use this instead: writefln("%s","311abc%20-%20tmb[1]")

Works the same way as with printf (in C).

--anders

This seems to be a common source of mistakes, and maybe something should be done about it. We could for example have two additional functions called write()/writeln(), which do not accept format strings, but otherwise works as writef() and writefln()? Nick
Jan 31 2005
next sibling parent reply =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Nick wrote:

The first argument to writef is *not* a string, but a format specifier.
Thus, you need to use this instead: writefln("%s","311abc%20-%20tmb[1]")

Works the same way as with printf (in C).

This seems to be a common source of mistakes, and maybe something should be done about it. We could for example have two additional functions called write()/writeln(), which do not accept format strings, but otherwise works as writef() and writefln()?

Yes, seems like a good idea. In C, there is "puts" which does writeln. Such a "stupid" function should also be faster than the full writef ? I believe OutputStream has a bunch of overloaded "write()" functions, but there are no such alternatives in std.stdio as far as I can tell. I'm also wondering how you output an ubyte[] full of 8-bit characters ? Using "%s" just gives the "invalid UTF-8 sequence" error with writef... Input and output to non-Unicode/UTF is something of a pain, in general. But I suppose I can do it on the stdin/stdout streams instead of stdio. --anders
Jan 31 2005
parent reply "Walter" <newshound digitalmars.com> writes:
"Anders F Björklund" <afb algonet.se> wrote in message
news:ctldek$ldr$1 digitaldaemon.com...
 I'm also wondering how you output an ubyte[] full of 8-bit characters ?
 Using "%s" just gives the "invalid UTF-8 sequence" error with writef...

std.c.stdio.fputs should do the trick.
Jan 31 2005
parent reply =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Walter wrote:
 "Anders F Björklund" <afb algonet.se> wrote in message
 news:ctldek$ldr$1 digitaldaemon.com...
 
I'm also wondering how you output an ubyte[] full of 8-bit characters ?
Using "%s" just gives the "invalid UTF-8 sequence" error with writef...

std.c.stdio.fputs should do the trick.

printf worked too, I was just wondering if I could do it with writef. --anders
Jan 31 2005
parent reply "Walter" <newshound digitalmars.com> writes:
"Anders F Björklund" <afb algonet.se> wrote in message
news:ctm4u8$1ka2$1 digitaldaemon.com...
 Walter wrote:
 "Anders F Björklund" <afb algonet.se> wrote in message
 news:ctldek$ldr$1 digitaldaemon.com...

I'm also wondering how you output an ubyte[] full of 8-bit characters ?
Using "%s" just gives the "invalid UTF-8 sequence" error with writef...

std.c.stdio.fputs should do the trick.

printf worked too, I was just wondering if I could do it with writef.

Not with writef, because the routines writef relies on assume UTF.
Jan 31 2005
parent =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Walter wrote:

I'm also wondering how you output an ubyte[] full of 8-bit characters ?
Using "%s" just gives the "invalid UTF-8 sequence" error with writef...

std.c.stdio.fputs should do the trick.


Not with writef, because the routines writef relies on assume UTF.

Okay, so if I want to, for instance, convert strings then I need to go byte level with D - or call upon the C standard library... Since D only supports Unicode, and not any legacy encodings. Got it. Here is the code I used, after conversion:
         ubyte[] test = (args.length > 1) ? cast(ubyte[]) args[1] : TEST;
         char[] str = decode_string(test, mapping);
         writefln("UTF-8: %s", str);
         
         ubyte[] enc = encode_string(str, mapping);
         std.stream.stdout.writeString("8-bit: ");
         foreach(ubyte b; enc) std.stream.stdout.write(b);
         std.stream.stdout.writeLine("");

The second *could* be replaced with printf, but I wanted to do it within the D library... It works OK, for translating to and from a ISO-8859-1 console. (for Latin-1, the "mapping" lookup is very simple one-to-one) --anders PS. Here were my translator functions:
 /// converts a 8-bit charset encoding string into unicode
 char[] decode_string(ubyte[] string, wchar[256] mapping);

 /// converts a unicode string into 8-bit charset encoding
 ubyte[] encode_string(char[] string, wchar[256] mapping);


Feb 01 2005
prev sibling parent reply =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Nick wrote:

 This seems to be a common source of mistakes, and maybe something should be
done
 about it. We could for example have two additional functions called
 write()/writeln(), which do not accept format strings, but otherwise works as
 writef() and writefln()?

Here's one trivial implementation of those:
 import std.stdio;
 import std.stream;
 
 void write(char[] str)
 {
      std.stream.stdout.writeString(str);
 }
 
 void writeln(char[] str)
 {
      std.stream.stdout.writeLine(str);
 }
 
 void main()
 {
    writef("*** Hall\u00e5, ");
    writefln("V\u00e4rlden! ***");
    write("%%% Hall\u00e5, ");
    writeln("V\u00e4rlden! %%%");
 }

It doesn't work for wchar[] or dchar[], though ? (You can't overload them, because of the literals) --anders
Jan 31 2005
parent reply Nick <Nick_member pathlink.com> writes:
In article <ctlfea$nh7$1 digitaldaemon.com>,
=?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= says...
Nick wrote:

 This seems to be a common source of mistakes, and maybe something should be
done
 about it. We could for example have two additional functions called
 write()/writeln(), which do not accept format strings, but otherwise works as
 writef() and writefln()?

Here's one trivial implementation of those: [snip]

I was thinking more in the line of functions which can print anything, not just strings. So writing eg. # int i, j; # char[] str; # ... # writeln(i, str, j, " hello"); would do exactly the same as # writefln(i, str, j, " hello"); except it would be safe if str contains any '%'-characters. Nick
Jan 31 2005
parent reply Sebastian Beschke <s.beschke gmx.de> writes:
Nick schrieb:
 I was thinking more in the line of functions which can print anything, not just
 strings. So writing eg.
 
 # int i, j;
 # char[] str;
 # ...
 # writeln(i, str, j, " hello");
 
 would do exactly the same as
 
 # writefln(i, str, j, " hello");
 
 except it would be safe if str contains any '%'-characters.

This would mean adding another way to accomplish exactly the same as writef does, which in general is a bad idea. It isn't easier to explain to a noob the difference between write/writef than between writef(str) and writef("%s", str). -Sebastian
Jan 31 2005
parent Nick <Nick_member pathlink.com> writes:
In article <ctlqte$17hd$1 digitaldaemon.com>, Sebastian Beschke says...
This would mean adding another way to accomplish exactly the same as 
writef does, which in general is a bad idea.

Why is that a bad idea? (I'm not contesting that it is, just asking.)
It isn't easier to explain to a noob the difference between write/writef 
than between writef(str) and writef("%s", str).

Sure it is. You say "Use write/writeln for output, eg. writeln(a,b,c). If you need specially formatted output, use the writef/writefln, eg. writefln("%-20s %5d", s, i);" But saying that you can't write eg. int i; cdouble d; MyNumberClass m; .. writefln(i, d, m); because m.toString might in theory return a '%' seems pretty counter-intuitive too me. Nick
Jan 31 2005
prev sibling parent reply Rev <Rev_member pathlink.com> writes:
In article <cth8jr$ni2$1 digitaldaemon.com>,
=?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= says...
Kramer wrote:

 I'm guessing writef got tripped up because the % sign is used as part of a
 format string for writef.  I tried taking the file name and putting single,
 double and the wysiwyg identifier "r" in front of it and tried to print it with
 writef, but I still get the error.  Is there any way around this?  I wouldn't
 have been able to anticipate thie file name on my system, so, maybe writef is
 not a good candidate for what I'm doing?

The first argument to writef is *not* a string, but a format specifier. Thus, you need to use this instead: writefln("%s","311abc%20-%20tmb[1]") Works the same way as with printf (in C). --anders

Your suggestion is good, but I think this an actual bug! Atleast in the case of "WYSIWYG" strings. If r" " or its equivalent, ` `, identifies a WYSIWYG string, then it shouldn't matter what the content of that string is. I should be able to place any number of format specifiers or escape sequence in that sring and print out excactly what I fed it. Atleast that's what I'd expect from a so-called "WYSIWYG" string. Rev
Jan 31 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Mon, 31 Jan 2005 21:51:31 +0000 (UTC), Rev <Rev_member pathlink.com>  
wrote:
 In article <cth8jr$ni2$1 digitaldaemon.com>,
 =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= says...
 Kramer wrote:

 I'm guessing writef got tripped up because the % sign is used as part  
 of a
 format string for writef.  I tried taking the file name and putting  
 single,
 double and the wysiwyg identifier "r" in front of it and tried to  
 print it with
 writef, but I still get the error.  Is there any way around this?  I  
 wouldn't
 have been able to anticipate thie file name on my system, so, maybe  
 writef is
 not a good candidate for what I'm doing?

The first argument to writef is *not* a string, but a format specifier. Thus, you need to use this instead: writefln("%s","311abc%20-%20tmb[1]") Works the same way as with printf (in C). --anders

Your suggestion is good, but I think this an actual bug! Atleast in the case of "WYSIWYG" strings. If r" " or its equivalent, ` `, identifies a WYSIWYG string, then it shouldn't matter what the content of that string is. I should be able to place any number of format specifiers or escape sequence in that sring and print out excactly what I fed it. Atleast that's what I'd expect from a so-called "WYSIWYG" string.

I see what you're saying.. however: I think the issue is with what a WYSIWIG string means/does. Currently it means that "escape sequences" will be treated literally. You took that to mean '%' was not evaluated/interpreted by writef, however, technically '%' is not an "escape sequence" but a "format specifier". r"a%b" and "a%b" are identical as neither contains an escape sequence. r"a\b" and "a\b" are not identical as the \ is literal in the former and escaped in the latter. Regan
Jan 31 2005
parent reply Rev <Rev_member pathlink.com> writes:
In article <opslhmtbtt23k2f5 ally>, Regan Heath says...
On Mon, 31 Jan 2005 21:51:31 +0000 (UTC), Rev <Rev_member pathlink.com>  
wrote:
 In article <cth8jr$ni2$1 digitaldaemon.com>,
 =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= says...
 Kramer wrote:

 I'm guessing writef got tripped up because the % sign is used as part  
 of a
 format string for writef.  I tried taking the file name and putting  
 single,
 double and the wysiwyg identifier "r" in front of it and tried to  
 print it with
 writef, but I still get the error.  Is there any way around this?  I  
 wouldn't
 have been able to anticipate thie file name on my system, so, maybe  
 writef is
 not a good candidate for what I'm doing?

The first argument to writef is *not* a string, but a format specifier. Thus, you need to use this instead: writefln("%s","311abc%20-%20tmb[1]") Works the same way as with printf (in C). --anders

Your suggestion is good, but I think this an actual bug! Atleast in the case of "WYSIWYG" strings. If r" " or its equivalent, ` `, identifies a WYSIWYG string, then it shouldn't matter what the content of that string is. I should be able to place any number of format specifiers or escape sequence in that sring and print out excactly what I fed it. Atleast that's what I'd expect from a so-called "WYSIWYG" string.

I see what you're saying.. however: I think the issue is with what a WYSIWIG string means/does. Currently it means that "escape sequences" will be treated literally. You took that to mean '%' was not evaluated/interpreted by writef, however, technically '%' is not an "escape sequence" but a "format specifier". r"a%b" and "a%b" are identical as neither contains an escape sequence. r"a\b" and "a\b" are not identical as the \ is literal in the former and escaped in the latter. Regan

IMHO, WYSIWYG strings should ignore both "escape sequences" and "format specifiers". WYSIWYG does not mean "What You See Is What You Get: except for format specifiers." If I wanted to use format specifiers then I would not (and definitely should not) be using a WYSIWYG string. This is not only a rational way to think about WYSIWYG but also intuitive and consistent with both definition and user expectation. Rev
Feb 01 2005
next sibling parent reply =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Rev wrote:

 IMHO, WYSIWYG strings should ignore both "escape sequences" and "format
 specifiers". WYSIWYG does not mean "What You See Is What You Get: except for
 format specifiers." If I wanted to use format specifiers then I would not (and
 definitely should not) be using a WYSIWYG string. This is not only a rational
 way to think about WYSIWYG but also intuitive and consistent with both
 definition and user expectation.

It would be more logical to have an output function that ignores format specifiers, than to try an bolt that attribute onto a char[]/wchar[]... The current "writef" is useful, but it's a little surprising that you need to double up every "%%" that you *didn't* intend as a formatter ? And I was wrong about the "first parameter", format specifiers can occur any time in a writef parameter list. So writef(100,"%") doesn't work. :( Just adding a matching std.stdio.write and std.stdio.writeln would be nice. Tried to do one myself, but the variadic args are a huge pain... Probably needs an extra argument to std.format.doFormat, or something ? ("pay attention to format characters", with a default of "please do") Just so that you can deactivate the '%' parsing, without rewriting all of the ugly little bits and warts of the big doFormat switch. So you can do something simple, like:
 import std.stdio;
 
 void main()
 {
    writeln("%%% Hello, World! %%%");
 } 

Without having it choke on the percentages. --anders
Feb 01 2005
parent "Regan Heath" <regan netwin.co.nz> writes:
On Tue, 01 Feb 2005 13:36:10 +0100, Anders F Björklund <afb algonet.se>  
wrote:
 Rev wrote:

 IMHO, WYSIWYG strings should ignore both "escape sequences" and "format
 specifiers". WYSIWYG does not mean "What You See Is What You Get:  
 except for
 format specifiers." If I wanted to use format specifiers then I would  
 not (and
 definitely should not) be using a WYSIWYG string. This is not only a  
 rational
 way to think about WYSIWYG but also intuitive and consistent with both
 definition and user expectation.

It would be more logical to have an output function that ignores format specifiers, than to try an bolt that attribute onto a char[]/wchar[]...

I agree, a WYSIWYG version of writef.
 The current "writef" is useful, but it's a little surprising that you
 need to double up every "%%" that you *didn't* intend as a formatter ?

Not in my opinion, as that is part of the contract of the function.
 And I was wrong about the "first parameter", format specifiers can occur  
 any time in a writef parameter list. So writef(100,"%") doesn't work. :(

I know, I found this out a little while back and it surprised me that more people haven't complained. AFAIKS it means that it's impossible, without verifying all strings you pass in some way, to guarantee there isn't a miscreant %s present. Possible solutions: 1- make the first parameter the only format string, easy, but not nice. 2- have some way to identify a format string. ideally static hard coded strings would be format strings and string variables passed wouldn't, but could be identified as such explicitly. 3- verify every string passed somehow.
 Just adding a matching std.stdio.write and std.stdio.writeln would be  
 nice. Tried to do one myself, but the variadic args are a huge pain...

 Probably needs an extra argument to std.format.doFormat, or something ?
 ("pay attention to format characters", with a default of "please do")

 Just so that you can deactivate the '%' parsing, without rewriting
 all of the ugly little bits and warts of the big doFormat switch.

 So you can do something simple, like:

 import std.stdio;
  void main()
 {
    writeln("%%% Hello, World! %%%");
 }

Without having it choke on the percentages.

I'm sure someone here has the time and ability to produce one for us. :) Regan
Feb 01 2005
prev sibling parent "Regan Heath" <regan netwin.co.nz> writes:
On Tue, 1 Feb 2005 12:19:26 +0000 (UTC), Rev <Rev_member pathlink.com>  
wrote:
 In article <opslhmtbtt23k2f5 ally>, Regan Heath says...
 On Mon, 31 Jan 2005 21:51:31 +0000 (UTC), Rev <Rev_member pathlink.com>
 wrote:
 In article <cth8jr$ni2$1 digitaldaemon.com>,
 =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= says...
 Kramer wrote:

 I'm guessing writef got tripped up because the % sign is used as part
 of a
 format string for writef.  I tried taking the file name and putting
 single,
 double and the wysiwyg identifier "r" in front of it and tried to
 print it with
 writef, but I still get the error.  Is there any way around this?  I
 wouldn't
 have been able to anticipate thie file name on my system, so, maybe
 writef is
 not a good candidate for what I'm doing?

The first argument to writef is *not* a string, but a format specifier. Thus, you need to use this instead: writefln("%s","311abc%20-%20tmb[1]") Works the same way as with printf (in C). --anders

Your suggestion is good, but I think this an actual bug! Atleast in the case of "WYSIWYG" strings. If r" " or its equivalent, ` `, identifies a WYSIWYG string, then it shouldn't matter what the content of that string is. I should be able to place any number of format specifiers or escape sequence in that sring and print out excactly what I fed it. Atleast that's what I'd expect from a so-called "WYSIWYG" string.

I see what you're saying.. however: I think the issue is with what a WYSIWIG string means/does. Currently it means that "escape sequences" will be treated literally. You took that to mean '%' was not evaluated/interpreted by writef, however, technically '%' is not an "escape sequence" but a "format specifier". r"a%b" and "a%b" are identical as neither contains an escape sequence. r"a\b" and "a\b" are not identical as the \ is literal in the former and escaped in the latter. Regan

IMHO, WYSIWYG strings should ignore both "escape sequences" and "format specifiers".

I disagree (more below)
 WYSIWYG does not mean "What You See Is What You Get: except for format  
 specifiers."

Correct, it doesn't. Not here or anywhere else that I know of. You're mixing two concepts here, the contents of the string, and what the function does with those contents. WYSIWYG strings, mean exactly that, the strings contents are exactly what you see. However, writef is not a 'WYSIWYG function'. It's design and contract specify that it will treat a '%' as a "format specifier", and it does.
 If I wanted to use format specifiers then I would not (and definitely  
 should not) be using a WYSIWYG string.

Again I think you're confusing the contents of the string, which is WYSIWYG, and the contract of the function.
 This is not only a rational way to think about WYSIWYG but also  
 intuitive and consistent with both
 definition and user expectation.

*If* writef was a WYSIWYG function then I'd agree with you. Regan
Feb 01 2005
prev sibling parent pragma <pragma_member pathlink.com> writes:
In article <cth792$mc8$1 digitaldaemon.com>, Kramer says...
Is there any way around this?  

This will reproduce the error:
# import std.stdio;
#
# void main()
# {
#     writef("311abc%20-%20tmb[1]");
# }

Try this instead:
 writefln("%s","311abc%20-%20tmb[1]");

- EricAnderton at yahoo
Jan 29 2005