Index: libcfa/src/containers/string.cfa
===================================================================
--- libcfa/src/containers/string.cfa	(revision 9ca5e56e849c1f5d92f3eb82c58fb599933090d4)
+++ libcfa/src/containers/string.cfa	(revision b5e725a08cfb0cd8ca08fc4c9afb6f82a6db8f82)
@@ -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 b5e725a08cfb0cd8ca08fc4c9afb6f82a6db8f82)
@@ -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 b5e725a08cfb0cd8ca08fc4c9afb6f82a6db8f82)
@@ -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 b5e725a08cfb0cd8ca08fc4c9afb6f82a6db8f82)
@@ -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
