detecting '\n' without using getline

Please support our C++ advertiser: Intel Parallel Studio Home
Reply

Join Date: Sep 2007
Posts: 8
Reputation: iaindb is an unknown quantity at this point 
Solved Threads: 0
iaindb's Avatar
iaindb iaindb is offline Offline
Newbie Poster

detecting '\n' without using getline

 
1
  #1
Sep 7th, 2007
Hi all,

I've written a configuration file parser, which is just basically gets a bunch of rules, each ended by ';'

To get the rules I use a loop:
  1. ifstream cfgFile;
  2. for (int iI = 0; iI < iMAX_MONITORS; iI ++) {
  3. ...
  4. cfgFile >> someFloat;
  5. // test for error
  6. cfgFile >> someInt;
  7. // test for error
  8. ...
  9. }

and so on, pretty standard stuff. The problem is, every time I detect an error, all I can do is print the rule number, and not the line number, which means I have to then count the rules by hand to find the erroneous line, because >> ignores whitespace. Is there any way around this short of rewriting the function to use getline?

Is there some stream builtin that will give me a character number, or line number where I am up to in the stream?

All I can think of currently is to use a macro to get the input, and check for '\n':

(I've removed the \ to make it look nicer, and I haven't tested it, but you get the idea
  1. #define EXPECT_INPUT(input, msg)
  2. {
  3. cfgFile >> input;
  4. if (!cfgFile) {
  5. if (cfgFile.eof ()) {
  6. break;
  7. }
  8. printf ("error \"%s\" in config file, rule %d, line %d.", msg, iI + 1, iLines + 1);
  9. cfgFile.clear();
  10. cfgFile.ignore(std::numeric_limits<streamsize>::max(),';');
  11. break;
  12. }
  13. do {
  14. c=cfgFile.peek();
  15. if (c == '\n') iLines ++;
  16. if (c==' ' || c=='\n') c=cfgFile.get();
  17. } while (c==' ' || c=='\n');
  18. }

and then just do:

EXPECT_INPUT (someFloat, "a number");
EXPECT_INPUT (charArray, "a string");

which makes my function look nice, except that I have to use an ugly macro. Mayby I should just use getline - thoughts?

thanks heaps.
--
Perfection is reached, not when there is no longer anything to add, but when there is no longer anything to take away.
-- Antoine de Saint-Exupery
Reply With Quote Quick reply to this message  
Join Date: Dec 2005
Posts: 5,850
Reputation: Salem has a reputation beyond repute Salem has a reputation beyond repute Salem has a reputation beyond repute Salem has a reputation beyond repute Salem has a reputation beyond repute Salem has a reputation beyond repute Salem has a reputation beyond repute Salem has a reputation beyond repute Salem has a reputation beyond repute Salem has a reputation beyond repute Salem has a reputation beyond repute 
Solved Threads: 751
Team Colleague
Salem's Avatar
Salem Salem is offline Offline
Void main'ers are DOOMed

Re: detecting '\n' without using getline

 
0
  #2
Sep 7th, 2007
Step 1 would be to redo that horrible macro as a proper C++ function.

There are very few valid reasons (struggling to think of one ATM) where using a function-like macro is the best approach. Inline functions and templates cover a lot of the things which were done as macros in C.

Step 2: Then consider implementing a small configFile class, which hides some of the detail (how the file is read), and can provide line:column information via a suitable member function.

Whether you then use getline(), or the code you have then no longer matters to the bulk of the code.

You could then implement other value services, say read a rule.
Reply With Quote Quick reply to this message  
Join Date: Sep 2007
Posts: 8
Reputation: iaindb is an unknown quantity at this point 
Solved Threads: 0
iaindb's Avatar
iaindb iaindb is offline Offline
Newbie Poster

Re: detecting '\n' without using getline

 
0
  #3
Sep 10th, 2007
OK I re-wrote the macro as a function template, however I came into a problem, which the macro didn't have:

I passed as an arg to the macro an extra "test":
  1. #define EXPECT_INPUT(input, test)
  2. {
  3. cfgFile >> input;
  4. if (!cfgFile || test) {
  5. if (cfgFile.eof ()) {
  6. break;
  7. }
  8. ...
and then for the macro call I could say: EXPECT_INPUT(someChar, (someChar != 'a')); and the test would be done inline, but I can't do that with a function, because the arguement is evaluated before it's passed:
  1. template <class input>
  2. void HandleInput (ifstream *poF, input &oI, bool bTest2 = false)
  3. {
  4. (*poF) >> oI;
  5. ...
  6. }
  7. ...
  8. void HandleInput (cfgFile, someChar, someChar != 'a');
How do I overcome this? Don't move the test to the function template? Seems like a bit of code duplication.

Also, it still hasn't overcome how I detect a '\n' and hence count lines!

Do I use tellg()? Or the concept from my original macro of sucking up and testing whitespace, but in the function template?

Your brutal honesty is appreciated

thanks again.
--
Perfection is reached, not when there is no longer anything to add, but when there is no longer anything to take away.
-- Antoine de Saint-Exupery
Reply With Quote Quick reply to this message  
Join Date: Sep 2007
Posts: 8
Reputation: iaindb is an unknown quantity at this point 
Solved Threads: 0
iaindb's Avatar
iaindb iaindb is offline Offline
Newbie Poster

Re: detecting '\n' without using getline

 
0
  #4
Sep 10th, 2007
OK, the "test" is easy, I can just set up a parameter with a default value
  1. template <class input>
  2. void HandleInput (ifstream *poF, input &oI, char test = '\0')
  3. ...
  4. if (test != '\0')
  5. ...
as it turns out all my tests were only when I am reading a single character. But still not sure about a more elegant way of detecting '\n'.
--
Perfection is reached, not when there is no longer anything to add, but when there is no longer anything to take away.
-- Antoine de Saint-Exupery
Reply With Quote Quick reply to this message  
Join Date: Dec 2006
Posts: 1,089
Reputation: vijayan121 is a name known to all vijayan121 is a name known to all vijayan121 is a name known to all vijayan121 is a name known to all vijayan121 is a name known to all vijayan121 is a name known to all 
Solved Threads: 164
vijayan121 vijayan121 is offline Offline
Veteran Poster

Re: detecting '\n' without using getline

 
0
  #5
Sep 10th, 2007
>> and then for the macro call I could say ... and the test would be done inline,
>> but I can't do that with a function, because the arguement is evaluated before it's passed:

use a function object (or a pointer to a function; not as flexible or efficient) to encapsulate a unit of procedural code.

>> still not sure about a more elegant way of detecting '\n'.

use composition; wrap a helper class which counts lines around the stream.
  1. #include <iostream>
  2. #include <fstream>
  3. #include <functional>
  4. using namespace std ;
  5.  
  6. template< typename ISTREAM > struct line_count_wrapper
  7. {
  8. explicit line_count_wrapper( ISTREAM& s ) : stm(s), line(1) {}
  9. int current_line() const { return line ; }
  10. template< typename T > line_count_wrapper<ISTREAM>& operator>> ( T& v )
  11. {
  12. eatwhite() ;
  13. stm >> v ;
  14. return *this ;
  15. }
  16. bool operator!() { return !stm ; }
  17. operator void* () { return stm ; }
  18. private:
  19. int line ;
  20. ISTREAM& stm ;
  21. void eatwhite()
  22. {
  23. char ch ;
  24. while( stm.get(ch) && isspace(ch) )
  25. if( ch == '\n' ) ++line ;
  26. if(stm) stm.putback(ch) ;
  27. }
  28. // non-copyable; do do not define these
  29. line_count_wrapper( const line_count_wrapper& ) ;
  30. void operator= ( const line_count_wrapper& ) ;
  31. };
  32.  
  33. template< typename LCSTM, typename T, typename TEST > inline
  34. bool handle_input( LCSTM& stm, T& input, TEST test_error, const char* msg )
  35. {
  36. if(!( stm >> input ) ) return false ;
  37. if ( test_error(input) )
  38. {
  39. cerr << "error " << msg << " at line " << stm.current_line() << '\n' ;
  40. return false ;
  41. }
  42. return true ;
  43. }
  44.  
  45. int main()
  46. {
  47. int i ;
  48. double d ;
  49. ifstream file( "config.txt" ) ;
  50. line_count_wrapper<istream> stm(file) ;
  51. while( stm )
  52. {
  53. handle_input( stm, i, bind2nd( equal_to<int>(), 0 ), "int is zero" ) ;
  54. handle_input( stm, d, bind2nd( less<double>(), 0.0 ), "double is -ve" ) ;
  55. }
  56. }
  57. /** config.txt
  58. 45 89.3
  59. 34 -23.7
  60. 0
  61. 56.7
  62. 0 -34.8 23
  63. 42.5
  64. 8 -67.2
  65. */
  66. /** output
  67. error double is -ve at line 2
  68. error int is zero at line 3
  69. error int is zero at line 5
  70. error double is -ve at line 5
  71. error double is -ve at line 7
  72. */
you could use boost.iostream and boost.lambda; the code would then look more elegant.
Reply With Quote Quick reply to this message  
Join Date: Aug 2005
Posts: 5,273
Reputation: iamthwee is a splendid one to behold iamthwee is a splendid one to behold iamthwee is a splendid one to behold iamthwee is a splendid one to behold iamthwee is a splendid one to behold iamthwee is a splendid one to behold iamthwee is a splendid one to behold iamthwee is a splendid one to behold 
Solved Threads: 378
Featured Poster
iamthwee's Avatar
iamthwee iamthwee is offline Offline
Posting Expert

Re: detecting '\n' without using getline

 
0
  #6
Sep 10th, 2007
Isn't looking for '\n' going to have problems on different os. I mean isn't newlines defined differently for macs, linux and windows operating systems?
Reply With Quote Quick reply to this message  
Join Date: Dec 2006
Posts: 1,089
Reputation: vijayan121 is a name known to all vijayan121 is a name known to all vijayan121 is a name known to all vijayan121 is a name known to all vijayan121 is a name known to all vijayan121 is a name known to all 
Solved Threads: 164
vijayan121 vijayan121 is offline Offline
Veteran Poster

Re: detecting '\n' without using getline

 
1
  #7
Sep 10th, 2007
c provides escape sequences '\n' and '\r'. contrary to popular belief, these are not required to be equivalent to specific control characters. the c standard guarantees that 1. each of these escape sequences maps to a unique implementation-defined number that can be stored in a single char. 2. when writing a file in text mode, '\n' is transparently translated to the native newline sequence on the the system (which may be longer than one character). when reading in text mode, the native newline sequence is translated back to '\n'. (in binary i/o mode, no such translation is performed.) c++ also provides L'\n' and L'\r'; these are the wchar_t equivalents.

so, looking for '\n' is correct if the char_type of the stream is a char. to also handle other char_types (eg. wchar_t), basic_ios<char_type,traits_types>::widen('\n') could be used.
Last edited by vijayan121; Sep 10th, 2007 at 3:43 pm.
Reply With Quote Quick reply to this message  
Join Date: Aug 2005
Posts: 5,273
Reputation: iamthwee is a splendid one to behold iamthwee is a splendid one to behold iamthwee is a splendid one to behold iamthwee is a splendid one to behold iamthwee is a splendid one to behold iamthwee is a splendid one to behold iamthwee is a splendid one to behold iamthwee is a splendid one to behold 
Solved Threads: 378
Featured Poster
iamthwee's Avatar
iamthwee iamthwee is offline Offline
Posting Expert

Re: detecting '\n' without using getline

 
0
  #8
Sep 10th, 2007
Interesting, so what's the best solution, or is using '\n' ok?
Reply With Quote Quick reply to this message  
Join Date: Dec 2006
Posts: 1,089
Reputation: vijayan121 is a name known to all vijayan121 is a name known to all vijayan121 is a name known to all vijayan121 is a name known to all vijayan121 is a name known to all vijayan121 is a name known to all 
Solved Threads: 164
vijayan121 vijayan121 is offline Offline
Veteran Poster

Re: detecting '\n' without using getline

 
0
  #9
Sep 10th, 2007
using '\n' is ok if we are sure that the char_type of the stream is a char. to also take care of all char_types (including user-defined char_types), we should modify the code to
  1. void eatwhite()
  2. {
  3. typedef typename ISTREAM::char_type char_type ;
  4. ctype<char_type> facet =
  5. use_facet< ctype<char_type> >( stm.getloc() ) ;
  6. char_type ch ;
  7. while( stm.get(ch) && facet.is( ctype_base::space, ch ) )
  8. if( ch == facet.widen('\n') ) ++line ;
  9. if(stm) stm.putback(ch) ;
  10. }
this may not be very appropriate code to show to a student who is just starting out with c++.
Last edited by vijayan121; Sep 10th, 2007 at 4:19 pm.
Reply With Quote Quick reply to this message  
Join Date: Aug 2005
Posts: 5,273
Reputation: iamthwee is a splendid one to behold iamthwee is a splendid one to behold iamthwee is a splendid one to behold iamthwee is a splendid one to behold iamthwee is a splendid one to behold iamthwee is a splendid one to behold iamthwee is a splendid one to behold iamthwee is a splendid one to behold 
Solved Threads: 378
Featured Poster
iamthwee's Avatar
iamthwee iamthwee is offline Offline
Posting Expert

Re: detecting '\n' without using getline

 
0
  #10
Sep 10th, 2007
I can't seem to get your example to work, what am I doing wrong?

  1. thwee@thwee-desktop:~$ g++ -Wall pedantic.cc
  2. pedantic.cc: In constructor ‘line_count_wrapper<ISTREAM>::line_count_wrapper(ISTREAM&) [with ISTREAM = std::basic_istream<char, std::char_traits<char> >]’:
  3. pedantic.cc:50: instantiated from here
  4. pedantic.cc:20: warning: ‘line_count_wrapper<std::basic_istream<char, std::char_traits<char> > >::stm’ will be initialized after
  5. pedantic.cc:19: warning: ‘int line_count_wrapper<std::basic_istream<char, std::char_traits<char> > >::line
  6. pedantic.cc:8: warning: when initialized here
??
Reply With Quote Quick reply to this message  
Reply

This thread is more than three months old.
Perhaps start a new thread instead?
Message:




Views: 2971 | Replies: 11
Thread Tools Search this Thread



Tag cloud for C++
About Us | Contact Us | Advertise | DaniWeb | Acceptable Use Policy | RSS Feed

©2003 - 2009 DaniWeb® LLC