www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - string to thread

reply "monarch_dodra" <monarchdodra gmail.com> writes:
In C++ (not C), when you wanted to parse a string, you were 
supposed to put the string inside a stream (std::stringstream), 
and then parse that new stream stream.

As a general rule, stringstream also allowed abstracting a string 
into a more generic stream.

I did not find anything equivalent in D. Did I just miss it?

How is a user supposed to parse a string in D?

I thought I was supposed to use "formattedRead", but was told 
that was actually more meant for library writers. Is there an 
alternative? Just trying to learn.
Sep 08 2012
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, September 08, 2012 11:01:50 monarch_dodra wrote:
 In C++ (not C), when you wanted to parse a string, you were
 supposed to put the string inside a stream (std::stringstream),
 and then parse that new stream stream.
 
 As a general rule, stringstream also allowed abstracting a string
 into a more generic stream.
 
 I did not find anything equivalent in D. Did I just miss it?
 
 How is a user supposed to parse a string in D?
 
 I thought I was supposed to use "formattedRead", but was told
 that was actually more meant for library writers. Is there an
 alternative? Just trying to learn.
If you were to operate on a string in a manner similar to a stream, you'd be operating on it as a range, and there are a lot of range-based functions in Phobos. But if you want to specifically parse a range of characters, then use std.conv.parse: http://dlang.org/phobos/std_conv.html#parse If you want to know more about ranges, then this is currently the best tutorial on them: http://ddili.org/ders/d.en/ranges.html - Jonathan M Davis
Sep 08 2012
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Saturday, 8 September 2012 at 09:10:30 UTC, Jonathan M Davis 
wrote:
 If you were to operate on a string in a manner similar to a 
 stream, you'd be
 operating on it as a range, and there are a lot of range-based 
 functions in
 Phobos. But if you want to specifically parse a range of 
 characters, then use
 std.conv.parse:

 http://dlang.org/phobos/std_conv.html#parse

 If you want to know more about ranges, then this is currently 
 the best
 tutorial on them:

 http://ddili.org/ders/d.en/ranges.html

 - Jonathan M Davis
Hum, parse. Looks useful. No need to create a temp stream like in C++ then. Good. Thanks for the info. That said, is the "abstraction" itself available? Say *someone* wrote an xml parser, and the public interface expects to operate on a "File". Now, supposing I don't have a file, but all I have is a string, is it possible for someone pass that string the the function?
Sep 09 2012
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, September 09, 2012 12:21:59 monarch_dodra wrote:
 Hum, parse. Looks useful. No need to create a temp stream like in
 C++ then. Good. Thanks for the info.
 
 That said, is the "abstraction" itself available? Say *someone*
 wrote an xml parser, and the public interface expects to operate
 on a "File". Now, supposing I don't have a file, but all I have
 is a string, is it possible for someone pass that string the the
 function?
So, your asking if you can pass a string to a function which requires a file? Is that what you're asking? I don't see why that would work, given that the function expects a file. So, I must be misunderstanding something... But one of the key design philosophies in Phobos is to use range-based APIs for just about everything. So, we wouldn't have an XML parser which operated on a file. It would operate on a range, and if you wanted to operate on a file, you'd get a range over the file and pass it in rather than the file. That's actually one key area that std.io should improve over std.stdio once Steven finally finishes it (since right now, it's kind of hard to have a good range interface on a file - you've can operate on it as a range of lines or chunks or whatnot but not as a range of characters or bytes without creating some sort of wrapper). But by using ranges everywhere, it becomes a lot like the *nix command-line where you can take the output of any program and pipe it as input to another program, allowing you to chain programs and mix and match them, but it's with ranges and functions rather than pipes and programs. - Jonathan M Davis
Sep 09 2012
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Sunday, 9 September 2012 at 10:36:14 UTC, Jonathan M Davis 
wrote:
 On Sunday, September 09, 2012 12:21:59 monarch_dodra wrote:
 Hum, parse. Looks useful. No need to create a temp stream like 
 in
 C++ then. Good. Thanks for the info.
 
 That said, is the "abstraction" itself available? Say *someone*
 wrote an xml parser, and the public interface expects to 
 operate
 on a "File". Now, supposing I don't have a file, but all I have
 is a string, is it possible for someone pass that string the 
 the
 function?
So, your asking if you can pass a string to a function which requires a file? Is that what you're asking? I don't see why that would work, given that the function expects a file. So, I must be misunderstanding something...
No, not like that, that'd be stupid. In C++, a functions that can parse/unparse (serialize/deserialize) take a "stream" as an argument. That is c++'s paradigm. A stream, more often than not, is a filestream, or a standard input/output stream. Now every once in a while, you'd like to parse/print a string. To do this, you create a "stringstream". That's what I was asking about. More below:
 But one of the key design philosophies in Phobos is to use 
 range-based APIs
 for just about everything. So, we wouldn't have an XML parser 
 which operated
 on a file. It would operate on a range, and if you wanted to 
 operate on a file,
 you'd get a range over the file and pass it in rather than the 
 file. That's
 actually one key area that std.io should improve over std.stdio 
 once Steven
 finally finishes it (since right now, it's kind of hard to have 
 a good range
 interface on a file - you've can operate on it as a range of 
 lines or chunks or
 whatnot but not as a range of characters or bytes without 
 creating some sort
 of wrapper). But by using ranges everywhere, it becomes a lot 
 like the *nix
 command-line where you can take the output of any program and 
 pipe it as input
 to another program, allowing you to chain programs and mix and 
 match them, but
 it's with ranges and functions rather than pipes and programs.

 - Jonathan M Davis
Okay, that makes a sense to me. In c++, the paradigm is: *Streams for formating. *Iterators for algorithms. Two paradigms => object to go from string(pointer/iterator) to stream. And you are telling me that in D, everything is ranges, so there is no such need for a string stream. Everything operates on ranges, and you can (or should be able) to scare an range out of a stream, is this correct? -------- What bothers me though (and is the source of my confusion) is that in that case, shouldn't stdin and stdout also be ranges? Or at least, shouldn't there be a global equivalent symbol that is a range? Imagine teaching a program that asks a user his age, and prints it. This is what you would teach: (qualified names for making a point) -------- import std.stdio; void main() { int age; //What is your age? std.stdio.stdin.readf("%s", &age); std.stdio.stdout.writefln("Your age is %s", age); } -------- Now, the next logical step in the lesson, is to make things more generic with a function, right? Why stop at std[in|out]? -------- import std.stdio; import std.array; import std.format; void askAge(RangeIn, RangeOut)(RangeIn ri, RangeOut ro) { int age; //What is your age? ri.formattedRead("%s", &age); //This changed ro.formattedWrite("Your age is %s\n", age); //So did this } void main() { auto input = "42"; auto output = appender!string(); askAge(input, output); writeln(output.data); } -------- Instead of using std[in|out], I create an InputRange (a string), and an output range (an Appender!string). However, the methods readf/writefln have been changed! This is what is confusing me: I have been taught to work on streams, but I can't use that interface on strings/ranges :/
Sep 09 2012
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, September 09, 2012 14:13:26 monarch_dodra wrote:
 Okay, that makes a sense to me. In c++, the paradigm is:
 *Streams for formating.
 *Iterators for algorithms.
 
 Two paradigms => object to go from string(pointer/iterator) to
 stream.
 
 And you are telling me that in D, everything is ranges, so there
 is no such need for a string stream. Everything operates on
 ranges, and you can (or should be able) to scare an range out of
 a stream, is this correct?
Pretty much. Some functions will never operate on ranges, and in some cases it may make sense to have a way to do something with ranges and a way to do it without them, but in general, things are going to be range based, and we may not even _have_ streams, since a stream is effectively a range.
 What bothers me though (and is the source of my confusion) is
 that in that case, shouldn't stdin and stdout also be ranges? Or
 at least, shouldn't there be a global equivalent symbol that is a
 range?
More likely, there will be a way to get a range over stdin or stdout. That's pretty much how we deal with it now (stdin and stdout are Files and get whatever functions File has). It's just that we need a better range type on Files (something which is forward range of bytes or characters rather than ranges of lines or chunks). That'll come with std.io, but Steven has been too busy to finish it yet.
 Instead of using std[in|out], I create an InputRange (a string),
 and an output range (an Appender!string). However, the methods
 readf/writefln have been changed!
I'd point out that formattedRead and formattedWrite aren't really meant to be used directly in your typical program, so they're not a terribly great example. Regardless, _some_ functions will make more sense to _not_ be range- based, and even if something works better if it's range-based, some older stuff which was created before we started using ranges is going to stick around for backwards compatibility if nothing else. So, some things may get range-based replacements but still have the old versions around. Exactly what happens with input and output will depend heavily on what happens with std.io.
 This is what is confusing me: I have been taught to work on
 streams, but I can't use that interface on strings/ranges :/
I don't understand this remark. You mean that the ranges for operating on input and output properly don't exist yet? That's definitely true, but it will be fixed. If you mean something else, I don't know what you mean. Streams pretty much _are_ ranges. - Jonathan M Davis
Sep 09 2012
parent "armando sano" <armando.sano gmail.com> writes:
Is there any update on this? This question of the distinction 
between reading/writing to a file stream vs to a string seems 
recurrent.

I am interested in writing to a string and am wondering if there 
is a reason for having to use explicitly the convenience 
functions std.conv.text() (or to!string()) and 
std.string.format() when one would expect to be able to do:

string s;
int n = 10;
s.writeln("Hello ", n, " times"); // s == "Hello 10 times\n"
s.writefln("and %d times", n); // s == "Hello 10 times\nand 10 
times\n"

There is a simple way to emulate this by expanding on string:

struct sstring {
	string _s;
	alias _s this; // treat sstring just like _s, a string
	
	this(string literal = "") { _s = literal; }

	void write(Args...)(Args args) { foreach(a; args)  {_s ~= 
to!string(a);} }
	void writeln(Args...)(Args args) { this.write(args, '\n'); }
	void writef(Char, Args...)(in Char[] fmt, Args args) { _s ~= 
std.string.format(fmt, args); }
	void writefln(Char, Args...)(in Char[] fmt, Args args) { 
this.writef(fmt ~ '\n', args); }	
}

An 'sstring s;' can be then be used just like a normal 'string' 
with the addition of being able to use it as in the example above.

I suspect something similar could be done for reading.

Why the syntactic burden of having to use to!string, 
std.string.format, std.conv.parse etc?
Apr 22 2015