Index: libcfa/src/containers/string.cfa
===================================================================
--- libcfa/src/containers/string.cfa	(revision 9ca5e56e849c1f5d92f3eb82c58fb599933090d4)
+++ libcfa/src/containers/string.cfa	(revision d32679d538afe4b499d20b11c9407b3fc906cd84)
@@ -100,5 +100,5 @@
 
 ////////////////////////////////////////////////////////
-// Output
+// Input-Output
 
 ofstream & ?|?( ofstream & out, const string & this ) {
@@ -109,4 +109,9 @@
     (ofstream &)(out | (*this.inner)); ends( out );
 }
+
+ifstream & ?|?(ifstream &in, string &this) {
+    return in | (*this.inner); // read to internal string_res
+}
+
 
 ////////////////////////////////////////////////////////
Index: libcfa/src/containers/string.hfa
===================================================================
--- libcfa/src/containers/string.hfa	(revision 9ca5e56e849c1f5d92f3eb82c58fb599933090d4)
+++ libcfa/src/containers/string.hfa	(revision d32679d538afe4b499d20b11c9407b3fc906cd84)
@@ -55,4 +55,5 @@
 ofstream & ?|?(ofstream &out, const string &s);
 void ?|?(ofstream &out, const string &s);
+ifstream & ?|?(ifstream &in, string &s);
 
 // Concatenation
Index: libcfa/src/containers/string_res.cfa
===================================================================
--- libcfa/src/containers/string_res.cfa	(revision 9ca5e56e849c1f5d92f3eb82c58fb599933090d4)
+++ libcfa/src/containers/string_res.cfa	(revision d32679d538afe4b499d20b11c9407b3fc906cd84)
@@ -17,4 +17,5 @@
 #include "string_sharectx.hfa"
 #include "stdlib.hfa"
+#include <ctype.h>
 
 // Workaround for observed performance penalty from calling CFA's alloc.
@@ -206,4 +207,44 @@
 	(ofstream &)(out | s); ends( out );
 }
+
+// Input operator
+ifstream & ?|?(ifstream &in, string_res &s) {
+
+    // Reading into a temp before assigning to s is near zero overhead in typical cases because of sharing.
+    // If s is a substring of something larger, simple assignment takes care of that case correctly.
+    // But directly reading a variable amount of text into the middle of a larger context is not practical.
+    string_res temp;
+
+    // Read in chunks.  Often, one chunk is enough.  Keep the string that accumulates chunks last in the heap,
+    // so available room is rest of heap.  When a chunk fills the heap, force growth then take the next chunk.
+    for (;;) {
+        // Append dummy content to temp, forcing expansion when applicable (occurs always on subsequent loops)
+        // length 2 ensures room for at least one real char, plus scanf/pipe-cstr's null terminator
+        temp += "--";
+        assert( temp.Handle.ulink->EndVbyte == temp.Handle.s + temp.Handle.lnth );    // last in heap
+
+        // reset, to overwrite the appended "--"
+        temp.Handle.lnth -= 2;
+        temp.Handle.ulink->EndVbyte -= 2;
+
+        // rest of heap, less 1 byte for null terminator, is available to read into
+        int lenReadable = (char*)temp.Handle.ulink->ExtVbyte - temp.Handle.ulink->EndVbyte - 1;
+        assert (lenReadable >= 1);
+
+        // get bytes
+        in | wdi( lenReadable, temp.Handle.ulink->EndVbyte );
+        int lenWasRead = strlen(temp.Handle.ulink->EndVbyte);
+
+        // update metadata
+        temp.Handle.lnth += lenWasRead;
+        temp.Handle.ulink->EndVbyte += lenWasRead;
+
+      if (lenWasRead < lenReadable) break;
+    }
+
+    s = temp;
+    return in;
+}
+
 
 // Empty constructor
@@ -373,10 +414,9 @@
 }
 
-static string_res & assign_(string_res &this, const char* buffer, size_t bsize, const string_res & valSrc) {
-
-    // traverse the incumbent share-edit set (SES) to recover the range of a base string to which `this` belongs
-    string_res * shareEditSetStartPeer = & this;
-    string_res * shareEditSetEndPeer = & this;
-    for ( string_res * editPeer = this.shareEditSet_next; editPeer != &this; editPeer = editPeer->shareEditSet_next ) {
+// traverse the share-edit set (SES) to recover the range of a base string to which `this` belongs
+static void locateInShareEditSet( string_res &this, string_res *&shareEditSetStartPeer, string_res *&shareEditSetEndPeer ) {
+    shareEditSetStartPeer = & this;
+    shareEditSetEndPeer = & this;
+    for (string_res * editPeer = this.shareEditSet_next; editPeer != &this; editPeer = editPeer->shareEditSet_next) {
         if ( editPeer->Handle.s < shareEditSetStartPeer->Handle.s ) {
             shareEditSetStartPeer = editPeer;
@@ -386,4 +426,11 @@
         }
     }
+}
+
+static string_res & assign_(string_res &this, const char* buffer, size_t bsize, const string_res & valSrc) {
+
+    string_res * shareEditSetStartPeer;
+    string_res * shareEditSetEndPeer;
+    locateInShareEditSet( this, shareEditSetStartPeer, shareEditSetEndPeer );
 
     verify( shareEditSetEndPeer->Handle.s >= shareEditSetStartPeer->Handle.s );
Index: libcfa/src/containers/string_res.hfa
===================================================================
--- libcfa/src/containers/string_res.hfa	(revision 9ca5e56e849c1f5d92f3eb82c58fb599933090d4)
+++ libcfa/src/containers/string_res.hfa	(revision d32679d538afe4b499d20b11c9407b3fc906cd84)
@@ -101,4 +101,5 @@
 ofstream & ?|?(ofstream &out, const string_res &s);
 void ?|?(ofstream &out, const string_res &s);
+ifstream & ?|?(ifstream &in, string_res &s);
 
 // Concatenation
Index: tests/collections/.expect/string-istream.txt
===================================================================
--- tests/collections/.expect/string-istream.txt	(revision d32679d538afe4b499d20b11c9407b3fc906cd84)
+++ tests/collections/.expect/string-istream.txt	(revision d32679d538afe4b499d20b11c9407b3fc906cd84)
@@ -0,0 +1,50 @@
+The
+quick
+brown
+fox
+jumps
+over...the
+lazy
+dog.
+=
+The
+-
+quick
+--
+brown
+---
+fox
+----
+jumps
+-----
+over...the
+------
+lazy
+-------
+dog.
+--------
+white-box-use-up-1000-char-heap-with-each-320-char-line5678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+---------
+01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+----------
+01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+-----------
+01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+------------
+=
+The
+-
+quick
+--
+brown
+---
+fox
+----
+jumps
+-----
+over...the
+------
+lazy
+-------
+dog.
+--------
Index: tests/collections/.in/string-istream.txt
===================================================================
--- tests/collections/.in/string-istream.txt	(revision d32679d538afe4b499d20b11c9407b3fc906cd84)
+++ tests/collections/.in/string-istream.txt	(revision d32679d538afe4b499d20b11c9407b3fc906cd84)
@@ -0,0 +1,34 @@
+The quick  brown
+
+fox
+ jumps
+ 
+over...the
+	lazy
+	
+dog.
+=
+The quick  brown
+
+fox
+ jumps
+ 
+over...the
+	lazy
+	
+dog.
+white-box-use-up-1000-char-heap-with-each-320-char-line5678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+=
+The quick  brown
+
+fox
+ jumps
+ 
+over...the
+	lazy
+	
+dog.
+=
Index: tests/collections/string-istream.cfa
===================================================================
--- tests/collections/string-istream.cfa	(revision d32679d538afe4b499d20b11c9407b3fc906cd84)
+++ tests/collections/string-istream.cfa	(revision d32679d538afe4b499d20b11c9407b3fc906cd84)
@@ -0,0 +1,61 @@
+#include <iostream.hfa>
+#include <containers/string.hfa>
+#include <containers/string_res.hfa>
+
+
+void istream_cstr(void) {
+    char s1[999], s2[999], s3[999], si[999];
+    sin | wdi(999,s1) | wdi(999,s2) | wdi(999,s3);
+    sout | s1;
+    sout | s2;
+    sout | s3;
+    for(;;) {
+        sin | wdi(999,si);
+      if (si[0] == '=') break;
+        sout | si;
+    }
+}
+
+string accumulator;
+
+void reset_otherStringAction(void) {
+    accumulator = "";
+}
+void step_otherStringAction(void) {
+    string localAction = "--";
+    accumulator += localAction(0,1);
+    sout | accumulator;
+}
+
+void istream_string_res(void) {
+    string_res s1, s2, s3, si;
+    sin | s1 | s2 | s3;
+    sout | s1;        step_otherStringAction();
+    sout | s2;        step_otherStringAction();
+    sout | s3;        step_otherStringAction();
+    for(;;) {
+        sin | si;
+      if (size(si) > 0 && si[0] == '=') break;
+        sout | si;    step_otherStringAction();
+    }
+}
+
+void istream_string(void) {
+    string s1, s2, s3, si;
+    sin | s1 | s2 | s3;
+    sout | s1;        step_otherStringAction();
+    sout | s2;        step_otherStringAction();
+    sout | s3;        step_otherStringAction();
+    for(;;) {
+        sin | si;
+      if (size(si) > 0 && si[0] == '=') break;
+        sout | si;    step_otherStringAction();
+    }
+}
+
+
+int main() {
+    istream_cstr();         sout | "=";
+    istream_string_res();   sout | "=";  reset_otherStringAction();
+    istream_string();
+}
