7 /*======================================================================*//**
9 One line of text is stored in this class, with additional metadata to track
10 whether the text was terminated by an EoL (End of Line) sequence, and, if so,
11 what the EoL sequence was.
13 This is particularly useful for reading lines of text from a file or a socket
14 without having to go to additional efforts to track whether the final line is
15 terminated with an EoL sequence (since this class takes care of this detail).
17 By default, the EoL sequence may be <CR>, <LF>, <CRLF>, or
21 One of the challenges with the @c std::string class is that it doesn't
22 provide a way to differentiate between a empty line and @c NULL, unless one
23 uses pointers to @c std::string objects and sets thoes pointers to @c nullptr
24 which entails having to test for that, resulting in more code in various
25 places to test for multiple conditions, thus providing opportunities for more
26 unintended situations (a.k.a., bugs) ... and then there's the matter of how
27 to handle the variety of EoL sequences that persist due to the diversity of
28 Operating System standards, which complicates matters even more.
30 This class handles all of these details gracefully, and provides a thorough
31 variety of clearly-documented methods that ensure consistency whilst handling
32 the complexity of EoL sequences that may be 1 or 2 characters long and also
33 differentiating between blank and NULL lines, all without causing significant
34 increases in complexity in code.
37 - 2024-Nov-16 v1.00 Initial version
39 @author Randolf Richardson
40 *///=========================================================================
43 char _eol_sequence[3] = {'\0', '\0', '\0'}; // EoL sequence (default = none)
44 int _eol_size = 0; // Size of EoL sequence (default = 0)
45 bool _eol_LFCR = false; // Only TRUE if EoL is broken <LFCR> sequence
46 std::string _line; // Text data without EoL sequence
48 /*======================================================================*//**
50 Internal line-of-text intake method used by constructors and other methods.
51 *///=========================================================================
53 /// Line of text to embrace
55 /// Length of data (-1 = ASCIIZ string)
59 // --------------------------------------------------------------------------
61 // --------------------------------------------------------------------------
62 if (line == nullptr) { // A null string was provided, so just do a clear() to prevent a Null Pointer Exception
63 _eol_sequence[0] = '\0';
64 _eol_sequence[1] = '\0';
71 // --------------------------------------------------------------------------
72 // Measure size of line if an ASCIIZ string was indicated.
73 // --------------------------------------------------------------------------
74 if (len == -1) len = std::strlen(line);
75 if (len == 0) return; // Empty line provided, so our class-defaults will be correct
77 // --------------------------------------------------------------------------
78 // Locate single-character EoL sequence.
79 // --------------------------------------------------------------------------
80 char ch1 = line[len - 1];
81 if ((ch1 == 10) || (ch1 == 13)) {
82 _eol_sequence[0] = ch1;
85 if (len == 1) return; // Total string length is 1 character, so we only needed to check for CR or LF
87 // --------------------------------------------------------------------------
88 // Locate double-character of EoL sequence (ch1 is actually the last/second
89 // character, which means that ch0 is the penultimate character, but only if
91 // --------------------------------------------------------------------------
92 char ch0 = line[len - 2];
93 if (((ch0 == 10) && (ch1 == 13)) || ((ch0 == 13) && (ch1 == 10))) {
94 _eol_sequence[0] = ch0;
95 _eol_sequence[1] = ch1;
97 if (ch0 == 10) _eol_LFCR = true;
100 // --------------------------------------------------------------------------
101 // Assign line of text, without EoL sequence (if any) to the internal string.
102 // --------------------------------------------------------------------------
103 _line.assign(line, len - _eol_size);
105 }; // -x- void _rline -x-
108 /*======================================================================*//**
110 Instantiate an empty rline, with no EoL sequences present.
111 *///=========================================================================
112 rline() noexcept {}; // -x- constructor rline -x-
114 /*======================================================================*//**
116 Instantiate a new rline based on the supplied line of text. Multiple lines
117 embedded in the supplied string will be treated as a single line, and ignored
120 If @c line is a @c nullptr then it will be treated as an empty string without
121 any EoL sequence instead of throwing a Null Pointer Exception. This provides
122 an opportunity to write simpler code (see the @ref is_null method for the way
123 to check for this condition, which also happens to be the result of using the
124 default constructor or calling @ref clear).
126 *///=========================================================================
128 /// Line of text to embrace
130 /// Length of line (-1 == ASCIIZ string)
132 ) noexcept { _rline(line, len); }; // -x- constructor rline -x-
134 /*======================================================================*//**
136 Instantiate a new rline based on the supplied line of text. Multiple lines
137 embedded in the supplied string will be treated as a single line, and ignored
139 *///=========================================================================
141 /// Line of text to embrace
143 ) noexcept { _rline(line.c_str(), line.length()); }; // -x- constructor rline -x-
145 /*======================================================================*//**
147 Add the specified string (without checking whether it contains any EoL
148 character sequences) onto the line of text (before the EoL sequence).
149 @returns The same rline object so as to facilitate stacking
152 *///=========================================================================
154 /// The string to insert
155 const std::string str) {
158 }; // -x- rline* append -x-
160 /*======================================================================*//**
162 Assign a new line of text, the same way the constructor assigns it.
164 If @c line is a @c nullptr then it will be treated as an empty string without
165 any EoL sequence instead of throwing a Null Pointer Exception. This provides
166 an opportunity to write simpler code (see the @ref is_null method for the way
167 to check for this condition, which also happens to be the result of using the
168 default constructor or calling @ref clear).
169 @returns The same rline object so as to facilitate stacking
172 *///=========================================================================
174 /// Line of text to embrace
176 /// Length of line (-1 == ASCIIZ string)
180 // --------------------------------------------------------------------------
181 // Reset internal variables.
182 // --------------------------------------------------------------------------
183 _eol_sequence[0] = '\0';
184 _eol_sequence[1] = '\0';
188 // --------------------------------------------------------------------------
189 // Replace line of text.
190 // --------------------------------------------------------------------------
194 }; // -x- rline* assign -x-
196 /*======================================================================*//**
198 Assign a new line of text, the same way the constructor assigns it.
199 @returns The same rline object so as to facilitate stacking
202 *///=========================================================================
204 /// Line of text to embrace
208 // --------------------------------------------------------------------------
209 // Reset internal variables.
210 // --------------------------------------------------------------------------
211 _eol_sequence[0] = '\0';
212 _eol_sequence[1] = '\0';
216 // --------------------------------------------------------------------------
217 // Replace line of text.
218 // --------------------------------------------------------------------------
219 _rline(line.c_str(), line.length());
222 }; // -x- rline* assign -x-
224 /*======================================================================*//**
226 Reset all data and internal flags to the original state of the constructor
227 that has no parameters.
228 @returns The same rline object so as to facilitate stacking
229 *///=========================================================================
230 rline* clear() noexcept {
232 // --------------------------------------------------------------------------
233 // Reset internal variables.
234 // --------------------------------------------------------------------------
235 _eol_sequence[0] = '\0';
236 _eol_sequence[1] = '\0';
242 }; // -x- rline* clear -x-
244 /*======================================================================*//**
246 Indicate whether the line of text is empty. Whether an EoL sequence is
247 present is incidental since this only measures the size of the line of text
248 without regard for the presence of an EoL sequence.
249 @returns TRUE = Line of text is empty@nFALSE = Line of text is not empty
252 *///=========================================================================
253 bool empty() noexcept { return _line.empty(); }; // -x- bool empty -x-
255 /*======================================================================*//**
257 Obtain a copy of the EoL sequence.
258 @returns The EoL sequence as a string
260 *///=========================================================================
261 const std::string eol() noexcept { return _eol_sequence; }; // -x- std::string eol -x-
263 /*======================================================================*//**
265 Obtain a copy of the entire line of text, without the EoL sequence (albeit by
266 default, this can also be included by setting the @c include_eol flag).
267 @returns The line of text as a string
269 *///=========================================================================
270 const std::string get(
271 /// Whether to include the EoL sequence (default = FALSE)
272 const bool include_eol = false
273 ) noexcept { return include_eol ? _line + _eol_sequence : _line; }; // -x- std::string get -x-
275 /*======================================================================*//**
277 Indicate whether an EoL sequence was detected.
278 @returns TRUE = EoL sequence is present@nFALSE = No EoL sequence
280 *///=========================================================================
281 bool has_eol() noexcept { return _eol_size != 0; }; // -x- bool has_eol -x-
283 /*======================================================================*//**
285 Insert the specified string (without checking whether it contains any EoL
286 character sequences) into the line of text at the specified position.
287 @exception std::out_of_range When the position (@c pos) provided is out of
288 range (e.g., position is larger than the entire
290 @returns The same rline object so as to facilitate stacking
293 *///=========================================================================
295 /// Where to insert the supplied string (0 = insert before first character;
296 /// and use a negative number to set the position relative to the end of the
298 int position, // Must be "int" and not "size_t" because we size_t is unsigned
299 /// The string to insert
300 const std::string str) {
301 _line.insert(position >= 0 ? position : (_line.size() + position), str);
303 }; // -x- rline* insert -x-
305 /*======================================================================*//**
307 Indicate whether this line is non-existent, which means there is no text and
308 no EoL sequence. This is less than an an @ref empty() string when it
309 confirms that there literally are no characters at all.
310 @returns TRUE = null string@nFALSE = not null (has text and/or an EoL
313 *///=========================================================================
314 bool is_null() noexcept { return (_line.size() + _eol_size) == 0; }; // -x- bool is_null -x-
316 /*======================================================================*//**
318 Provides the length of the line of text without the EoL sequence.
320 This method is identital to the @ref size method.
321 @returns Number of bytes
323 *///=========================================================================
325 /// Whether to include the EoL sequence (default = FALSE)
326 const bool include_eol = false
327 ) noexcept { return include_eol ? _line.size() + _eol_size : _line.size(); }; // -x- size_t length -x-
329 /*======================================================================*//**
331 Provides the length of the line of text without the EoL sequence.
333 This method is identital to the @ref length method.
334 @returns Number of bytes
336 *///=========================================================================
338 /// Whether to include the EoL sequence (default = FALSE)
339 const bool include_eol = false
340 ) noexcept { return include_eol ? _line.size() + _eol_size : _line.size(); }; // -x- size_t size -x-
342 /*======================================================================*//**
344 Provides the length of the EoL sequence.
345 @returns Number of bytes (0 = no EoL string)
346 *///=========================================================================
347 size_t size_eol() noexcept { return _eol_size; }; // -x- size_t size_eol -x-
349 /*======================================================================*//**
351 Convert this @c rline to an @c std::string without the EoL sequence.
354 #include <iostream> // std::cout, std::cerr, std::endl, etc.
355 #include <string> // std::string
356 #include <randolf/rline>
358 int main(int argc, char *argv[]) {
359 rline rl("This is an example.\n");
361 std::cout << "\"" << s << "\"" << std::endl;
363 } // -x- int main -x-
366 @returns The line of text as an std::string object
368 *///=========================================================================
369 operator std::string() const noexcept { return _line; }; // -x- operator std::string -x-
371 /*======================================================================*//**
373 Support convenient streaming usage with std::cout, std::cerr, and friends.
374 @returns Line of text, without EoL sequence
375 *///=========================================================================
376 friend std::ostream& operator<< (
377 /// Output stream (provided automatically by std::cout and std::cerr)
379 /// Object class (matched by compiler)
380 rline const& c) { return o << (char*)c._line.c_str(); }; // -x- std::ostream& operator<< -x-
382 }; // -x- class rline -x-
384}; // -x- namespace randolf -x-