# HG changeset patch # User Lachlan Andrew # Date 1460281790 -36000 # Node ID fed1111e18998c34546a4ebecf88be039b754a17 # Parent fe0a6de805e4f09160ded882f0ec5c49500c792c textscan: eliminate ReturnOnError="continue" mode (bug #47538) * oct-stream.cc (textscan_format_list::read_first_row, textscan::scan_one) No longer skip failed fields. (textscan::do_scan): Do not terminate on no_conversions. (textscan::read_format_once): Only "fail" if field wasn't empty. (textscan::parse_options): No longer parse "continue" option. * file-io.cc (Ftextscan): Update docstring and self-tests. diff -r fe0a6de805e4 -r fed1111e1899 libinterp/corefcn/file-io.cc --- a/libinterp/corefcn/file-io.cc Tue Apr 12 21:05:50 2016 +1000 +++ b/libinterp/corefcn/file-io.cc Sun Apr 10 19:49:50 2016 +1000 @@ -1337,8 +1337,7 @@ @item literals\n\ In addition the format may contain literal character strings; these will be\n\ skipped during reading. If the input string does not match this literal,\n\ -the processing terminates, unless @qcode{\"ReturnOnError\"} is set to\n\ -@qcode{\"continue\"}.\n\ +the processing terminates.\n\ @end table\n\ \n\ Parsed words corresponding to the first specifier are returned in the first\n\ @@ -1456,10 +1455,8 @@ \n\ @item @qcode{\"ReturnOnError\"}\n\ If set to numerical 1 or true, return normally as soon as an error is\n\ -encountered, such as trying to read a string using @qcode{%f}. If set to 0\n\ -or false, return an error and no data. If set to @qcode{\"continue\"}\n\ -(default), textscan attempts to continue reading beyond the location;\n\ -however, this may cause the parsing to get out of sync.\n\ +encountered, such as trying to read a string using @qcode{%f}.\n\ +If set to 0 or false, return an error and no data.\n\ \n\ @item @qcode{\"Whitespace\"}\n\ Any character in @var{value} will be interpreted as whitespace and trimmed;\n\ @@ -1690,18 +1687,13 @@ %! str = "1 2 3\n4 s 6"; %! fprintf (fid, str); %! fseek (fid, 0, "bof"); -%! c = textscan (fid, "%f %f %f"); -%! fseek (fid, 0, "bof"); -%! d = textscan (fid, "%f %f %f", "ReturnOnError", 1); +%! c = textscan (fid, "%f %f %f", "ReturnOnError", 1); %! fseek (fid, 0, "bof"); %! fclose (fid); %! unlink (f); -%! u = textscan (str, "%f %f %f"); -%! v = textscan (str, "%f %f %f", "ReturnOnError", 1); -%! assert (c, {[1;4], [2;NaN], [3;6]}); -%! assert (d, {[1;4], [2], [3]}); -%! assert (u, {[1;4], [2;NaN], [3;6]}); -%! assert (v, {[1;4], [2], [3]}); +%! u = textscan (str, "%f %f %f", "ReturnOnError", 1); +%! assert (c, {[1;4], [2], [3]}); +%! assert (u, {[1;4], [2], [3]}); %!test %! ## Check ReturnOnError @@ -1726,14 +1718,11 @@ %! fid = fopen (f, "w+"); %! fprintf (fid, "1 s 3\n4 5 6"); %! fseek (fid, 0, "bof"); -%! c = textscan (fid, ""); -%! fseek (fid, 0, "bof"); -%! d = textscan (fid, "", "ReturnOnError", 1); +%! c = textscan (fid, "", "ReturnOnError", 1); %! fseek (fid, 0, "bof"); %! fclose (fid); %! unlink (f); -%! assert (c, {[1;4], [NaN;5], [3;6]}); -%! assert (d, {1}); +%! assert (c, {1}); %!test %! ## Check ReturnOnError with empty fields diff -r fe0a6de805e4 -r fed1111e1899 libinterp/corefcn/oct-stream.cc --- a/libinterp/corefcn/oct-stream.cc Tue Apr 12 21:05:50 2016 +1000 +++ b/libinterp/corefcn/oct-stream.cc Sun Apr 10 19:49:50 2016 +1000 @@ -2459,31 +2459,15 @@ if (ds.eof ()) break; - // If we don't continue after a conversion error, then - // unless this was a missing value (i.e., followed by a delimiter), + // Unless this was a missing value (i.e., followed by a delimiter), // return with an error status. - if (ts.return_on_error < 2) + ts.skip_delim (ds); + if (ds.no_progress ()) { - ts.skip_delim (ds); - if (ds.no_progress ()) - { - retval = 4; - break; - } - already_skipped_delim = true; + retval = 4; + break; } - else // skip offending field - { - std::ios::iostate state = ds.rdstate (); - ds.clear (); // clear to allow read pointer to advance - - std::string dummy; - textscan_format_elt fe ("", first_line.length ()); - ts.scan_string (ds, fe, dummy); - - progress = (dummy.length ()); - ds.setstate (state); - } + already_skipped_delim = true; val = ts.empty_value.scalar_value (); @@ -2534,7 +2518,7 @@ empty_value (octave_NaN), exp_chars ("edED"), header_lines (0), treat_as_empty (), treat_as_empty_len (0), whitespace (" \b\t"), eol1 ('\r'), eol2 ('\n'), - return_on_error (2), collect_output (false), + return_on_error (1), collect_output (false), multiple_delims_as_one (false), default_exp (true), numeric_delim (false), lines (0) { } @@ -2674,7 +2658,7 @@ row_idx(0) = row; err = read_format_once (is, fmt_list, out, row_idx, done_after); - if (err > 0 || ! is || (lines >= ntimes && ntimes > -1)) + if ((err & ~1) > 0 || ! is || (lines >= ntimes && ntimes > -1)) break; } } @@ -3393,24 +3377,8 @@ } } - if (is.fail ()) - { - if (! fmt.discard) - ov = do_cat_op (ov, empty_value, row); - - // If we are continuing after errors, skip over this field - if (return_on_error == 2) - { - std::ios::iostate state = is.rdstate (); - is.clear (); // clear to allow read pointer to advance - - std::string dummy; - scan_string (is, fmt, dummy); - - is.setstate (state); - } - } - + if (is.fail () & ! fmt.discard) + ov = do_cat_op (ov, empty_value, row); } else { @@ -3510,10 +3478,10 @@ } else { - if (return_on_error < 2) + is.clear (is.rdstate () & ~std::ios::failbit); + + if (!is.eof () && ~is_delim (is.peek ())) this_conversion_failed = true; - - is.clear (is.rdstate () & ~std::ios::failbit); } if (! elem->discard) @@ -3696,10 +3664,7 @@ } else if (param == "returnonerror") { - if (args(i+1).is_string () && args(i+1).string_value () == "continue") - return_on_error = 2; - else - return_on_error = args(i+1).xbool_value ("%s: ReturnOnError must be logical, numeric, or the string \"continue\"", who.c_str ()); + return_on_error = args(i+1).xbool_value ("%s: ReturnOnError must be logical or numeric", who.c_str ()); } else if (param == "whitespace") {