# HG changeset patch
# User Philip Nienhuis <prnienhuis@users.sf.net>
# Date 1320615597 -3600
# Node ID 417123bc901aee59dd68da787a1814a67f54e52a
# Parent  0a28c49cb47783017d1a6f7b55ebd4de4fe89be6
Always separate numeric fields by whitespace too; gracefully catch too wide literals & fixed width format conversion specifiers

diff -r 0a28c49cb477 -r 417123bc901a scripts/io/strread.m
--- a/scripts/io/strread.m	Sun Nov 06 20:08:17 2011 +0000
+++ b/scripts/io/strread.m	Sun Nov 06 22:39:57 2011 +0100
@@ -1,4 +1,5 @@
 ## Copyright (C) 2009-2011 Eric Chassande-Mottin, CNRS (France)
+## Parts Copyright (C) 2011 Philip Nienhuis <prnienhuis@users.sf.net>
 ##
 ## This file is part of Octave.
 ##
@@ -123,8 +124,10 @@
 ## Any character in @var{value} will be used to split @var{str} into words
 ## (default value = any whitespace).
 ##
-## @item "emptyvalue"
-## Parts of the output where no word is available is filled with @var{value}.
+## @item "emptyvalue":
+## Value to return for empty numeric values in non-whitespace delimited data.
+## The default is NaN. When the data type does not support NaN
+## (int32 for example), then default is zero.
 ##
 ## @item "multipledelimsasone"
 ## Treat a series of consecutive delimiters, without whitespace in between,
@@ -144,6 +147,8 @@
 ## trimmed; the string defining whitespace must be enclosed in double
 ## quotes for proper processing of special characters like \t.
 ## The default value for whitespace = " \b\r\n\t" (note the space).
+## Unless whitespace is set to '' (empty) AND at least one "%s" format
+## conversion specifier is supplied, a space is always part of whitespace.
 ##
 ## @end table
 ##
@@ -226,7 +231,7 @@
         endswitch
       case "delimiter"
         delimiter_str = varargin{n+1};
-        if (is_sq_string (delimiter_str))
+        if (strcmp (typeinfo (delimiter_str), "sq_string"))
           delimiter_str = do_string_escapes (delimiter_str);
         endif
       case "emptyvalue"
@@ -235,13 +240,13 @@
         warning ('strread: property "expchars" is not implemented');
       case "whitespace"
         white_spaces = varargin{n+1};
-        if (is_sq_string (white_spaces))
+        if (strcmp (typeinfo (white_spaces), "sq_string"))
           white_spaces = do_string_escapes (white_spaces);
         endif
       ## The following parameters are specific to textscan and textread
       case "endofline"
         eol_char = varargin{n+1};
-        if (is_sq_string (eol_char))
+        if (strcmp (typeinfo (eol_char), "sq_string"))
           eol_char = do_string_escapes (eol_char);
         endif
       case "returnonerror"
@@ -329,8 +334,14 @@
   endif
 
   if (! isempty (white_spaces))
-    ## Remove any delimiter chars from white_spaces list
-    white_spaces = setdiff (white_spaces, delimiter_str);
+    ## For numeric fields, whitespace is always a delimiter, but not for text fileds
+    if (isempty (strfind (format, "%s")))
+      ## Add whitespace to delimiter set
+      delimiter_str = unique ([white_spaces delimiter_str]); int8(delimiter_str)
+    else
+      ## Remove any delimiter chars from white_spaces list
+      white_spaces = setdiff (white_spaces, delimiter_str);
+    endif
   endif
   if (isempty (delimiter_str))
     delimiter_str = " ";
@@ -344,27 +355,20 @@
 
   pad_out = 0;
   ## Trim whitespace if needed
-  ## FIXME: This is very complicated.  Can this be simplified with regexprep?
   if (! isempty (white_spaces))
     ## Check if trailing "\n" might signal padding output arrays to equal size
     ## before it is trimmed away below
     if ((str(end) == 10) && (nargout > 1))
       pad_out = 1;
     endif
-    ## Remove repeated white_space chars.  First find white_space positions
-    idx = strchr (str, white_spaces);
-    ## Find repeated white_spaces
-    idx2 = ! (idx(2:end) - idx(1:end-1) - 1);
-    ## Set all whitespace chars to spaces
-    ## FIXME: this implies real spaces are always part of white_spaces
-    str(idx) = ' ';
-    ## Set all repeated white_space to \0
-    str(idx(idx2)) = "\0";
-    str = strsplit (str, "\0");
-    ## Reconstruct trimmed str
-    str = cell2mat (str);
-    ## Remove leading & trailing space, but preserve delimiters.
-    str = strtrim (str);
+    ## Condense all repeated whitespace into one single space
+    ## FIXME: this will also fold repeated whitespace in a char field
+    rxp_wsp = sprintf ("[%s]+", white_spaces);
+    str = regexprep (str, rxp_wsp, ' ');
+    ## Remove possible leading space at string
+    if (str(1) == 32)
+       str = str(2:end);
+    endif
     ## FIXME: Double strrep on str is enormously expensive of CPU time.
     ## Can this be eliminated
     ## Wipe leading and trailing whitespace on each line (it may be delimiter too)
@@ -410,7 +414,7 @@
   idy = find (idf | idg);
 
   ## If needed, split up columns in three steps:
-  if (! isempty (idy))
+  if (! isempty (idy) || mxd_fmt)
     ## Try-catch because complexity of strings to read can be infinite
     #try
 
@@ -434,7 +438,13 @@
             iwrdp += length (fmt_words{ii});
             if (iwrdp > iwrdl)
               ## Parse error.  Literal extends beyond delimiter (word boundary)
-              error ("strread: Literal '%s' (fmt spec # %d) does not match data", fmt_words{ii}, ii);
+              warning ("strread: literal '%s' (fmt spec # %d) does not match data", ...
+                fmt_words{ii}, ii);
+              ## Word assumed to be completely "used up". Next word
+              ++iwrd; iwrdp = 0;
+              if (ii < numel (fmt_words))
+                iwrdl = length (words{iwrd});
+              endif
             elseif (iwrdp == iwrdl)
               ## Word completely "used up". Next word
               ++iwrd; iwrdp = 0;
@@ -449,8 +459,12 @@
             iwrdp += floor ...
              (str2double (fmt_words{ii}(regexp(fmt_words{ii}, '\d') : end-1)));
             if (iwrdp > iwrdl)
-              ## Error. Field extends beyond word boundary.
-              error ("strread: Field width '%s' (fmt spec # %d) extends beyond word limit", fmt_words{ii}, ii);
+              ## Match error. Field extends beyond word boundary.
+              warning  ...
+              ("strread: field width '%s' (fmt spec # %d) extends beyond actual word limit", ...
+                 fmt_words{ii}, ii);
+              ## Assume word to be completely "used up".  Next word
+              ++iwrd; iwrdp = 0; iwrdl = length (words{iwrd});
             elseif (iwrdp == iwrdl)
               ## Word completely "used up".  Next word
               ++iwrd; iwrdp = 0; iwrdl = length (words{iwrd});
@@ -556,8 +570,10 @@
 
         else
           ## Conv. specifier.  Peek if next fmt_word needs split from current column
-          if (ii < num_words_per_line && idf(ii+1))
-            if (! isempty (strfind (words{icol, 1}, fmt_words{ii+1})))
+          if (ii < num_words_per_line)
+            if (idf(ii+1) && (! isempty (strfind (words{icol, 1}, fmt_words{ii+1}))))
+              --icol;
+            elseif (idg(ii+1))
               --icol;
             endif
           endif
