8 /*======================================================================*//**
10 One line of text is stored in this class, with additional metadata to track
11 whether the text was terminated by an EoL (End of Line) sequence, and, if so,
12 what the EoL sequence was.
14 This is particularly useful for reading lines of text from a file or a socket
15 without having to go to additional efforts to track whether the final line is
16 terminated with an EoL sequence (since this class takes care of this detail).
18 By default, the EoL sequence may be <CR>, <LF>, <CRLF>, or
22 One of the challenges with the @c std::string class is that it doesn't
23 provide a way to differentiate between a empty line and @c NULL, unless one
24 uses pointers to @c std::string objects and sets thoes pointers to @c nullptr
25 which entails having to test for that, resulting in more code in various
26 places to test for multiple conditions, thus providing opportunities for more
27 unintended situations (a.k.a., bugs) ... and then there's the matter of how
28 to handle the variety of EoL sequences that persist due to the diversity of
29 Operating System standards, which complicates matters even more.
31 This class handles all of these details gracefully, and provides a thorough
32 variety of clearly-documented methods that ensure consistency whilst handling
33 the complexity of EoL sequences that may be 1 or 2 characters long and also
34 differentiating between blank and NULL lines, all without causing significant
35 increases in complexity in code.
38 - 2024-Nov-16 v1.00 Initial version
39 - 2025-Jan-05 v1.00 Added c_str(), data(), and append(char*, int) methods,
40 and the rline(std::string, std::string) constructor
41 - 2025-Jan-09 v1.00 Added @ref eol(char, char) and @ref eol(std::string)
42 methods for configuring a specific EoL sequence
43 - 2025-Jan-12 v1.00 Changed std::length_error to std::invalid_argument in
44 response to length being specified as a negative value
45 so this can more easily be handled with seperate code;
46 added the @c at() method, and [], ==, and != operators
47 - 2025-Jan-13 v1.00 Improved internal support for EoL flag indication
48 - 2025-Feb-03 v1.00 Increased use of references and pointers
49 - 2025-Feb-09 v1.00 Added a constructor, and @ref append(), @ref assign(),
50 and @ref to_vector(), methods, and, finally, also an
51 `operator std::vector<char>` method, all of which make
52 easy to work with data in `std::vector<char>` arrays
53 - 2025-Feb-10 v1.00 Added @ref clear_all() method (which complements the
55 - 2025-Feb-13 v1.00 Added @ref append(const char) method, and @ref first()
56 and @ref last() methods
58 @author Randolf Richardson
59 *///=========================================================================
62 std::string __line; // Text data without EoL sequence
63 char __eol_detected[3] = {'\0', '\0', '\0'}; // EoL sequence detected (default = none)
64 int __eol_detected_size = 0; // EoL sequence detected size (default = 0)
65 char __eol_standard[3] = {'\0', '\0', '\0'}; // EoL sequence standard (default = none/autodetect)
66 int __eol_standard_size = 0; // EoL sequence standard size (default = 0)
68 /*======================================================================*//**
70 Internal line-of-text intake method used by constructors and other methods.
71 @returns TRUE = EoL sequence was encountered@n
72 FALSE = EoL sequence is not present
73 *///=========================================================================
75 /// Line of text to embrace
77 /// Length of data (-1 = ASCIIZ string)
78 int len = -1) noexcept {
80 // --------------------------------------------------------------------------
82 // --------------------------------------------------------------------------
83 if (line == nullptr) return false; // A null string was provided, so we can avoid a lot of processing
85//std::cout << "__rline len=" << __line.size() << " data=[" << data << "]" << std::endl;
86 // --------------------------------------------------------------------------
87 // Measure size of line if an ASCIIZ string was indicated.
88 // --------------------------------------------------------------------------
89 if (len == -1) len = std::strlen(line);
90 if (len == 0) return false; // Empty line provided, so this is an easy finish
92 // --------------------------------------------------------------------------
93 // Pre-allocate memory in std::string so that we can write directly to the
94 // underlying array, which is the most efficient (fastest) way.
95 // --------------------------------------------------------------------------
96 int offset = __line.size();
97 __line.resize(offset + len); // In case of a dymamic realloc(), this must be done before calling __line.data()
98 char* __line_data = __line.data();
100//std::cout << "__rline len=" << len << " offset=" << offset << " line=[" << line << "]" << std::endl;
101//std::cout << "__rline len=" << len << " offset=" << offset << std::endl;
102 // --------------------------------------------------------------------------
103 // Detect existing (as in already-defined) EoL sequence while copying data.
104 // --------------------------------------------------------------------------
105 if (__eol_standard_size != 0) {
106//std::cout << "__eol_sequence_size != 0" << std::endl;
108 // --------------------------------------------------------------------------
109 // Internal variables.
110 // --------------------------------------------------------------------------
112 char e = __eol_standard[0];
115 // --------------------------------------------------------------------------
116 // Check for edge case with 2-character EoL sequence carries over from
117 // existing line data.
118 // --------------------------------------------------------------------------
119 if (offset > 0 && __eol_standard_size == 2) {
120 if (__line[offset - 1] == e && line[0] == __eol_standard[1]) {
121 __line.resize(__line.size() - 1); // Crop previous character
122 __eol_detected[0] = e;
123 __eol_detected[1] = line[0];
124 __eol_detected_size = __eol_standard_size;
127 } // -x- if !offset -x-
129//std::cout << "__rline len=" << len << " offset=" << offset << " line=[" << line << "]" << std::endl;
130 // --------------------------------------------------------------------------
132 // --------------------------------------------------------------------------
133 for (i = 0; i < len; i++) {
135 // --------------------------------------------------------------------------
136 // Obtain next character to absorb.
137 // --------------------------------------------------------------------------
140 // --------------------------------------------------------------------------
141 // EoL sequence detected. If this is a one-character EoL sequence, then it
142 // matches and we're done; or if this is a two-character EoL sequence, then
143 // it also must match; otherwise the sequence will be ignored and treated as
144 // a normal part of the text.
145 // --------------------------------------------------------------------------
147 if (__eol_standard_size == 1) {
148 __eol_detected[0] = e;//__eol_standard[0];
149 __eol_detected[1] = 0;//__eol_standard[1];
150 __eol_detected_size = 1;
153 if ((i + 1) < len && line[i + 1] == __eol_standard[1]) {
154 __eol_detected[0] = e;
155 __eol_detected[1] = __eol_standard[1];
156 __eol_detected_size = 2;
162 // --------------------------------------------------------------------------
163 // Append character to internal __line by writing directly to the underlying
164 // array (which is the most efficienty way to store it), then advance the
166 // --------------------------------------------------------------------------
167 __line_data[offset++] = c;
171 // --------------------------------------------------------------------------
172 // Automatic EoL sequence detection while copying data.
173 // --------------------------------------------------------------------------
174 } else { // This "else" statement is effectively the same as: if (eol_standard_size == 0)
175//std::cout << "__eol_sequence_size == 0" << std::endl;
177 // --------------------------------------------------------------------------
178 // Internal variables.
179 // --------------------------------------------------------------------------
183//std::cout << "__rline len=" << len << " offset=" << offset << " line=[" << line << "]" << std::endl;
184 // --------------------------------------------------------------------------
186 // --------------------------------------------------------------------------
187 for (i = 0; i < len; i++) {
189 // --------------------------------------------------------------------------
190 // Obtain next character to absorb.
191 // --------------------------------------------------------------------------
194 // --------------------------------------------------------------------------
195 // LF detected, so save it and exit the loop, while also advancing
196 // __eol_sequence_size (in two places if this is a broken LFCR sequence). We
197 // do need to compare "i" against "len" to prevent a 1-byte index overflow.
198 // --------------------------------------------------------------------------
199 if (c == 10) { // LF detected
200 __eol_detected_size = __eol_standard_size;
201 __eol_detected[__eol_detected_size++] = c;
202 __eol_standard[__eol_standard_size++] = c;
203 if ((i + 1) < len && line[i + 1] == 13) { // CR detected, so save it too
204 __eol_detected[__eol_detected_size++] = 13;
205 __eol_standard[__eol_standard_size++] = 13;
207//std::cout << "__eol_detected_size<LF>=" << __eol_detected_size << std::endl;
210 // --------------------------------------------------------------------------
211 // CR detected, so save it and exit the loop, while also advancing
212 // __eol_sequence_size (in two places if this is a valid CRLF sequence). We
213 // do need to compare "i" against "len" to prevent a 1-byte index overflow.
214 // --------------------------------------------------------------------------
215 } else if (c == 13) { // CR detected
216 __eol_detected_size = __eol_standard_size;
217 __eol_detected[__eol_detected_size++] = c;
218 __eol_standard[__eol_standard_size++] = c;
219 if ((i + 1) < len && line[i + 1] == 10) { // LF detected, so save it too
220 __eol_detected[__eol_detected_size++] = 10; // LF detected, so save it too
221 __eol_standard[__eol_standard_size++] = 10; // LF detected, so save it too
223//std::cout << "__eol_detected_size<CR>=" << __eol_detected_size << std::endl;
226 } // -x- if CR/LF -x-
228 // --------------------------------------------------------------------------
229 // Append character to internal __line by writing directly to the underlying
230 // array (which is the most efficienty way to store it), then advance the
232 // --------------------------------------------------------------------------
233 __line_data[offset++] = c;
234//std::cout << "c=" << c << " offset=" << offset << std::endl;
238 } // -x- if !__eol_standar_size -x-
240 // --------------------------------------------------------------------------
241 // The "offset" variable was updated by the loop to ensure it holds the
242 // accurate length of __line internally. The "i" variable was loop-local.
243 // --------------------------------------------------------------------------
244 __line.resize(offset);
245//std::cout << "offset=" << offset << " __line=[" << get() << "]" << std::endl;
247 return __eol_standard_size > 0; // TRUE = EoL sequence was encountered
248 } // -x- bool __rline -x-
251 /*======================================================================*//**
253 Instantiate an empty rline, with no EoL sequences present.
254 *///=========================================================================
255 rline() noexcept {} // -x- constructor rline -x-
257 /*======================================================================*//**
259 Instantiate a new rline based on the supplied line of text, ending early upon
260 encountering the first EoL sequence (use either the @ref size(bool) or the
261 @ref size_with_eol() method to obtain the total quantity of characters that
262 were absorbed, including the EoL sequence {if present}).
264 If no EoL sequence was encountered, the @ref has_eol() method will return
265 `false` even though the contents of the supplied line were copied (and are
266 still accessible) -- this is useful in loops reading lines of text that are
267 too long to store in memory with a single allocation (and either interpreting
268 the absorbed data somehow, or using a simple loop to skip through to the end
269 of the line before issuing an error that indicates the total line length).
271 If @c line is a @c nullptr then it will be treated as an empty string without
272 an EoL sequence, instead of throwing a Null Pointer Exception. This provides
273 an opportunity to write simpler code (see the @ref is_null method for the way
274 to check for this condition, which also happens to be the result of using the
275 default constructor or calling @ref clear).
282 *///=========================================================================
284 /// Line of text to embrace
286 /// Length of line (-1 == ASCIIZ string)
287 const size_t len = -1) noexcept {
289 } // -x- constructor rline -x-
291 /*======================================================================*//**
293 Instantiate a new rline based on the supplied line of text, ending early upon
294 encountering the first EoL sequence (use either the `size(true)` or the
295 `size_full()` method to obtain the total quantity of characters that were
296 absorbed, including the EoL sequence {if present}).
298 If no EoL sequence was encountered, the @ref has_eol() method will return
299 `false` even though the contents of the supplied line were copied (and are
300 still accessible) -- this is useful in loops reading lines of text that are
301 too long to store in memory with a single allocation (and either interpreting
302 the absorbed data somehow, or using a simple loop to skip through to the end
303 of the line before issuing an error that indicates the total line length).
309 *///=========================================================================
311 /// Line of text to embrace
312 const std::string& line,
313 /// Length of line (-1 == defer to length of line provided)
314 const size_t len = -1) noexcept {
315 __rline(line.data(), len == -1 ? line.length() : len);
316 } // -x- constructor rline -x-
318 /*======================================================================*//**
320 Instantiate a new rline based on the supplied line of text, ending early upon
321 encountering the first EoL sequence (the `size(true)` and `size_full()`
322 methods obtain the total quantity of characters that were absorbed, including
323 the EoL sequence {if present}).
325 If no EoL sequence was encountered, the @ref has_eol() method will return
326 `false` even though the contents of the supplied line were copied (and are
327 still accessible) -- this is useful in loops reading lines of text that are
328 too long to store in memory with a single allocation (and either interpreting
329 the absorbed data somehow, or using a simple loop to skip through to the end
330 of the line before issuing an error that indicates the total line length).
331 @throws std::length_error if the EoL sequence is longer than 2 characters
337 *///=========================================================================
339 /// Line of text to embrace, without the EoL sequence characters
340 const std::string& line,
341 /// The EoL sequence to impose as if it had been detected (may be only 1 or 2
343 const std::string& eol_sequence) {
345 // --------------------------------------------------------------------------
346 // Save EoL sequence while also performing a simple syntax check.
347 // --------------------------------------------------------------------------
348 switch (eol_sequence.size()) {
350 __eol_detected[0] = __eol_standard[0] = eol_sequence[0];
351 __eol_detected[1] = __eol_standard[1] = eol_sequence[1];
352 __eol_detected_size = __eol_standard_size = 2;
355 __eol_detected[0] = __eol_standard[0] = eol_sequence[0];
356 __eol_detected[1] = __eol_standard[1] = '\0';
357 __eol_detected_size = __eol_standard_size = 1;
360 __eol_detected[0] = __eol_standard[0] = '\0';
361 __eol_detected[1] = __eol_standard[1] = '\0';
362 __eol_detected_size = __eol_standard_size = 0;
364 deafult: // Any other length is invalid
365 throw std::length_error("eol_sequence is too long (maximum length is 2 characters)");
366 } // -x- switch eol_sequence.size -x-
368 // --------------------------------------------------------------------------
370 // --------------------------------------------------------------------------
371 __rline(line.data(), line.length());
373 } // -x- constructor rline -x-
375 /*======================================================================*//**
377 Instantiate a new rline based on the supplied line of text, ending early upon
378 encountering the first EoL sequence (use either the `size(true)` or the
379 `size_full()` method to obtain the total quantity of characters that were
380 absorbed, including the EoL sequence {if present}).
382 If no EoL sequence was encountered, the @ref has_eol() method will return
383 `false` even though the contents of the supplied line were copied (and are
384 still accessible) -- this is useful in loops reading lines of text that are
385 too long to store in memory with a single allocation (and either interpreting
386 the absorbed data somehow, or using a simple loop to skip through to the end
387 of the line before issuing an error that indicates the total line length).
393 *///=========================================================================
395 /// Line of text to embrace
396 const std::vector<char>& line,
397 /// Length of line (-1 == defer to length of line provided)
398 const size_t len = -1) noexcept {
399 __rline(line.data(), len == -1 ? line.size() : len);
400 } // -x- constructor rline -x-
402 /*======================================================================*//**
404 Add the specified character (without checking whether it's part of an EoL
405 character sequence) onto the line of text (before the EoL sequence).
406 @returns The same rline object so as to facilitate stacking
409 *///=========================================================================
411 /// The string to insert
412 const char character) {
413 char ch[] = { character, 0 };
416 } // -x- rline& append -x-
418 /*======================================================================*//**
420 Add the specified string (without checking whether it contains any EoL
421 character sequences) onto the line of text (before the EoL sequence).
422 @returns The same rline object so as to facilitate stacking
425 *///=========================================================================
427 /// The string to insert
429 /// Length of line (-1 == ASCIIZ string)
430 const size_t len = -1) {
433 } // -x- rline& append -x-
435 /*======================================================================*//**
437 Add the specified std::string (without checking whether it contains any EoL
438 character sequences) onto the line of text (before the EoL sequence).
439 @returns The same rline object so as to facilitate stacking
442 *///=========================================================================
444 /// The string to insert
445 const std::string& line,
446 /// Length of line (-1 == defer to length of line provided)
447 const size_t len = -1) {
448 __rline(line.data(), len == -1 ? line.length() : len);
450 } // -x- rline& append -x-
452 /*======================================================================*//**
454 Add the specified std::vector<char> (without checking whether it contains any
455 EoL character sequences) onto the line of text (before the EoL sequence).
456 @returns The same rline object so as to facilitate stacking
459 *///=========================================================================
461 /// The data to insert
462 const std::vector<char>& line,
463 /// Length of line (-1 == defer to length of line provided)
464 const size_t len = -1) {
465 __rline(line.data(), len == -1 ? line.size() : len);
467 } // -x- rline& append -x-
469 /*======================================================================*//**
471 Assign a new rline based on the supplied line of text, ending early upon
472 encountering the first EoL sequence (use either the @ref size(bool) or the
473 @ref size_with_eol() method to obtain the total quantity of characters that
474 were absorbed, including the EoL sequence {if present}).
476 If no EoL sequence was encountered, the @ref has_eol() method will return
477 `false` even though the contents of the supplied line were copied (and are
478 still accessible) -- this is useful in loops reading lines of text that are
479 too long to store in memory with a single allocation (and either interpreting
480 the absorbed data somehow, or using a simple loop to skip through to the end
481 of the line before issuing an error that indicates the total line length).
483 If @c line is a @c nullptr then it will be treated as an empty string without
484 an EoL sequence, instead of throwing a Null Pointer Exception. This provides
485 an opportunity to write simpler code (see the @ref is_null method for the way
486 to check for this condition, which also happens to be the result of using the
487 default constructor or calling @ref clear).
488 @returns The same rline object so as to facilitate stacking
496 *///=========================================================================
498 /// Line of text to embrace
500 /// Length of line (-1 == ASCIIZ string)
501 const size_t len = -1) noexcept {
503 // --------------------------------------------------------------------------
504 // Reset internal variables.
505 // --------------------------------------------------------------------------
508 // --------------------------------------------------------------------------
509 // Replace line of text.
510 // --------------------------------------------------------------------------
514 } // -x- rline& assign -x-
516 /*======================================================================*//**
518 Assign a new rline based on the supplied line of text, ending early upon
519 encountering the first EoL sequence (use either the @ref size(bool) or the
520 @ref size_with_eol() method to obtain the total quantity of characters that
521 were absorbed, including the EoL sequence {if present}).
523 If no EoL sequence was encountered, the @ref has_eol() method will return
524 `false` even though the contents of the supplied line were copied (and are
525 still accessible) -- this is useful in loops reading lines of text that are
526 too long to store in memory with a single allocation (and either interpreting
527 the absorbed data somehow, or using a simple loop to skip through to the end
528 of the line before issuing an error that indicates the total line length).
529 @returns The same rline object so as to facilitate stacking
536 *///=========================================================================
538 /// Line of text to embrace
539 const std::string& line,
540 /// Length of line (-1 == defer to length of line provided)
541 const size_t len = -1) noexcept {
543 // --------------------------------------------------------------------------
544 // Reset internal variables.
545 // --------------------------------------------------------------------------
548 // --------------------------------------------------------------------------
549 // Replace line of text.
550 // --------------------------------------------------------------------------
551 __rline(line.data(), len == -1 ? line.length() : len);
554 } // -x- rline& assign -x-
556 /*======================================================================*//**
558 Assign a new rline based on the supplied line of text, ending early upon
559 encountering the first EoL sequence (use either the @ref size(bool) or the
560 @ref size_with_eol() method to obtain the total quantity of characters that
561 were absorbed, including the EoL sequence {if present}).
563 If no EoL sequence was encountered, the @ref has_eol() method will return
564 `false` even though the contents of the supplied line were copied (and are
565 still accessible) -- this is useful in loops reading lines of text that are
566 too long to store in memory with a single allocation (and either interpreting
567 the absorbed data somehow, or using a simple loop to skip through to the end
568 of the line before issuing an error that indicates the total line length).
569 @returns The same rline object so as to facilitate stacking
576 *///=========================================================================
578 /// Line of text to embrace
579 const std::vector<char>& line,
580 /// Length of line (-1 == defer to length of line provided)
581 const size_t len = -1) noexcept {
583 // --------------------------------------------------------------------------
584 // Reset internal variables.
585 // --------------------------------------------------------------------------
588 // --------------------------------------------------------------------------
589 // Replace line of text.
590 // --------------------------------------------------------------------------
591 __rline(line.data(), len == -1 ? line.size() : len);
594 } // -x- rline& assign -x-
596 /*======================================================================*//**
598 Array-style access to the underlying std::string (without the EoL sequence).
600 The first element is at index 0.
601 @throws std::out_of_range if the index is out-of-range
606 *///=========================================================================
608 /// Index of character to access (0 = first element; negative index values
609 /// are calculated in reverse, starting with -1 as the final position)
612 // --------------------------------------------------------------------------
613 // Internal variables.
614 // --------------------------------------------------------------------------
615 int MAX = __line.length();
617 // --------------------------------------------------------------------------
619 // --------------------------------------------------------------------------
620 if (index >= MAX || 0 - index > MAX) throw std::out_of_range("index (position=" + std::to_string(index) + ") falls outside of the underlying string (length=" + std::to_string(MAX) + ")");
622 return __line[index >= 0 ? index : MAX - index];
625 /*======================================================================*//**
627 Reset all data (including the detected EoL sequence) while preserving
628 internal flags (including the established EoL standard).
629 @returns The same rline object so as to facilitate stacking
630 *///=========================================================================
631 rline& clear() noexcept {
633 // --------------------------------------------------------------------------
634 // Reset internal variables.
635 // --------------------------------------------------------------------------
636 __eol_detected[0] = '\0';
637 __eol_detected[1] = '\0';
638 __eol_detected_size = 0;
642 } // -x- rline& clear -x-
644 /*======================================================================*//**
646 Reset all data (including the detected EoL sequence) and reset internal flags
647 (including the established EoL standard) to be like the original state of the
648 constructor that has no parameters.
649 @returns The same rline object so as to facilitate stacking
650 *///=========================================================================
651 rline& clear_all() noexcept {
653 // --------------------------------------------------------------------------
654 // Reset internal variables.
655 // --------------------------------------------------------------------------
656 __eol_detected[0] = __eol_standard[0] = '\0';
657 __eol_detected[1] = __eol_standard[1] = '\0';
658 __eol_detected_size = __eol_standard_size = 0;
662 } // -x- rline& clear_all -x-
664 /*======================================================================*//**
666 Returns a pointer to the underlying string array, without the EoL sequence.
667 @returns Pointer to buffer (null terminated)
669 *///=========================================================================
670 const char* c_str() noexcept {
671 return __line.c_str();
672 } // -x- char* c_str -x-
674 /*======================================================================*//**
676 Returns a pointer to the underlying string array, without the EoL sequence.
677 @returns Pointer to buffer (not null terminated)
679 *///=========================================================================
680 char* data() noexcept {
681 return __line.data();
682 } // -x- char* data -x-
684 /*======================================================================*//**
686 Obtain a copy of the EoL sequence that was detected.
687 @returns A pointer to the EoL sequence as a @c char[] array
690 *///=========================================================================
691 const char* data_eol(
692 /// TRUE = return the detected EoL sequence (default) @n
693 /// FALSE = return the EoL sequence to use when detecting an EoL sequence
694 const bool detected = true) noexcept {
695 return detected ? __eol_detected : __eol_standard;
696 } // -x- char* data_eol -x-
698 /*======================================================================*//**
700 Returns a pointer to the underlying std::string object, without the EoL
702 @returns Pointer to buffer (not null terminated)
705 *///=========================================================================
706 std::string* data_string() noexcept {
708 } // -x- std::string* data -x-
710 /*======================================================================*//**
712 Indicate whether the line of text is empty. Whether an EoL sequence is
713 present is incidental since this only measures the size of the line of text
714 without regard for the presence of an EoL sequence.
715 @returns TRUE = Line of text is empty@n
716 FALSE = Line of text is not empty
719 *///=========================================================================
720 bool empty() noexcept {
721 return __line.empty();
722 } // -x- bool empty -x-
724 /*======================================================================*//**
726 Obtain a copy of the EoL sequence that was detected.
727 @returns The EoL sequence as a string
730 *///=========================================================================
731 const std::string eol(
732 /// TRUE = return the detected EoL sequence (default) @n
733 /// FALSE = return the EoL sequence to use when detecting an EoL sequence
734 const bool detected = true) noexcept {
735 return std::string(detected ? __eol_detected : __eol_standard);
736 } // -x- std::string& eol -x-
738 /*======================================================================*//**
740 Define the EoL sequence to use when detecting an EoL sequence (also clears
741 the detected EoL sequence, if present).
743 This method is the most efficient @c eol method to use for defining the
744 specific EoL sequence to use.
745 @returns The same rline object so as to facilitate stacking
747 *///=========================================================================
749 /// The first character of the EoL sequence (typically 10 or 13)@c
750 /// 10 = LF (Line Feed) @n
751 /// 13 = CR (Carriage Return) @n
752 /// 0 = enable EoL sequence auto-detection (this is the default in all
753 /// constructors, and is also set by the @ref clear method)
755 /// The second character of the EoL sequence (typically 10 or 13)@c
756 /// 10 = LF (Line Feed) @n
757 /// 13 = CR (Carriage Return) @n
758 /// 0 = indicate that this is not a two-character EoL sequence
759 const char c2 = 0) noexcept {
761 // --------------------------------------------------------------------------
762 // Save EoL sequence.
763 // --------------------------------------------------------------------------
764 __eol_detected[0] = __eol_detected[1] = 0;
765 __eol_standard[0] = c1;
766 __eol_standard[1] = c1 == 0 ? 0 : c2;
768 // --------------------------------------------------------------------------
769 // Calculate the size of the EoL sequence.
770 // --------------------------------------------------------------------------
771 __eol_detected_size = 0;
772 __eol_standard_size = 2;
773 if (c2 == 0) __eol_standard_size = 1;
774 if (c1 == 0) __eol_standard_size = 0;
777 } // -x- rline& eol -x-
779 /*======================================================================*//**
781 Define the EoL sequence to use when detecting an EoL sequence (also clears
782 the detected EoL sequence, if present).
783 @throws std::length_error if the EoL sequence is longer than 2 characters
784 @returns The same rline object so as to facilitate stacking
786 *///=========================================================================
788 /// The EoL sequence (may be only 1 or 2 characters long; or 0 characters to
789 /// enable EoL auto-detection, which is also the default in all constructors)
790 const std::string& eol_sequence) {
792 // --------------------------------------------------------------------------
793 // Save EoL sequence while also performing a simple syntax check.
794 // --------------------------------------------------------------------------
795 switch (eol_sequence.size()) {
797 __eol_detected[0] = __eol_detected[1] = 0;
798 __eol_standard[0] = eol_sequence[0];
799 __eol_standard[1] = eol_sequence[1];
800 __eol_detected_size = 0;
801 __eol_standard_size = 2;
804 __eol_detected[0] = __eol_detected[1] = 0;
805 __eol_standard[0] = eol_sequence[0];
806 __eol_standard[1] = '\0';
807 __eol_detected_size = 0;
808 __eol_standard_size = 1;
811 __eol_detected[0] = __eol_detected[1] = 0;
812 __eol_standard[0] = '\0';
813 __eol_standard[1] = '\0';
814 __eol_detected_size = 0;
815 __eol_standard_size = 0;
817 deafult: // Any other length is invalid
818 throw std::length_error("eol_sequence is too long (maximum length is 2 characters)");
819 } // -x- switch eol_sequence.size -x-
822 } // -x- rline& eol -x-
824 /*======================================================================*//**
826 Return the first character from the underlying std::string (not including the
828 @throws std::out_of_range if the underlying string is empty
833 *///=========================================================================
836 // --------------------------------------------------------------------------
838 // --------------------------------------------------------------------------
839 if (__line.empty()) throw std::out_of_range("first() is out of range because rline is empty");
841 return __line.front();
842 } // -x- char first -x-
844 /*======================================================================*//**
846 Obtain a copy of the entire line of text, without the EoL sequence (albeit by
847 default, this can also be included by setting the @c include_eol flag).
848 @returns The line of text as a string
851 *///=========================================================================
852 const std::string get(
853 /// Whether to include the EoL detected (default = FALSE)
854 const bool include_eol = false) noexcept {
855 return std::string(include_eol ? __line + __eol_detected : __line);
856 } // -x- std::string get -x-
858 /*======================================================================*//**
860 Indicate whether an EoL sequence was detected.
861 @returns TRUE = EoL sequence is present@n
862 FALSE = No EoL sequence
864 *///=========================================================================
865 bool has_eol() noexcept {
866 return __eol_detected_size != 0;
867 } // -x- bool has_eol -x-
869 /*======================================================================*//**
871 Indicate whether a broken EoL sequence detected of `<LFCR>` is being used as
873 @returns TRUE = Broken EoL sequence is present@n
874 FALSE = No broken EoL sequence
876 *///=========================================================================
878 /// TRUE = report on whether a broken EoL sequence was detected (default) @n
879 /// FALSE = report on whether a broken EoL sequence was specifically defined
880 /// as the EoL sequence to detect
881 const bool detected = true) noexcept {
882 if (true) return __eol_detected_size == 2 && __eol_detected[0] == 10 && __eol_detected[1] == 13;
883 else return __eol_standard_size == 2 && __eol_standard[0] == 10 && __eol_standard[1] == 13;
884 } // -x- bool has_LFCR -x-
886 /*======================================================================*//**
888 Insert the specified string (without checking whether it contains any EoL
889 character sequences) into the line of text at the specified position.
890 @exception std::out_of_range When the position (@c pos) provided is out of
891 range (e.g., position is larger than the entire
893 @returns The same rline object so as to facilitate stacking
896 *///=========================================================================
898 /// Where to insert the supplied string (0 = insert before first character;
899 /// and use a negative number to set the position relative to the end of the
901 const int position, // Must be "int" and not "size_t" because size_t is unsigned
902 /// The string to insert
903 const std::string& str,
904 /// Length of string (-1 == defer to length of string provided)
905 const size_t len = -1) {
906//std::cout << "str=[" << str << "] position=" << (position >= 0 ? position : __line.size() + position) << std::endl;
907 __line.insert((position >= 0 ? position : __line.size() + position),
910 len == -1 ? str.size() : len);
911//std::cout << "__line=[" << __line << "]" << std::endl;
913 } // -x- rline& insert -x-
915 /*======================================================================*//**
917 Indicate whether this line is non-existent, which means there is no text and
918 no detected EoL sequence. This is less than an an @ref empty() string when
919 it confirms that there literally are no characters at all.
920 @returns TRUE = null string@n
921 FALSE = not null (has text and/or an EoL
924 *///=========================================================================
925 bool is_null() noexcept {
926 return (__line.size() + __eol_detected_size) == 0;
927 } // -x- bool is_null -x-
929 /*======================================================================*//**
931 Return the last character from the underlying std::string (not including the
933 @throws std::out_of_range if the underlying string is empty
938 *///=========================================================================
941 // --------------------------------------------------------------------------
943 // --------------------------------------------------------------------------
944 if (__line.empty()) throw std::out_of_range("last() is out of range because rline is empty");
946 return __line.back();
947 } // -x- char last -x-
949 /*======================================================================*//**
951 Provides the length of the line of text without the EoL sequence.
953 This method is identital to the @ref size method.
954 @returns Number of bytes
956 *///=========================================================================
958 /// Whether to include the EoL sequence (default = FALSE)
959 const bool include_eol = false) noexcept {
960 return include_eol ? __line.size() + __eol_detected_size : __line.size();
961 } // -x- size_t length -x-
963 /*======================================================================*//**
965 Provides the length of the line of text without the EoL sequence.
967 This method is identital to the @ref length method.
968 @returns Number of bytes
972 *///=========================================================================
974 /// Whether to include the EoL sequence (default = FALSE)
975 const bool include_eol = false) noexcept {
976 return include_eol ? __line.size() + __eol_detected_size : __line.size();
977 } // -x- size_t size -x-
979 /*======================================================================*//**
981 Provides the length of the detected EoL sequence.
982 @returns Number of bytes (0 = no EoL string)
984 *///=========================================================================
986 /// TRUE = report on the size of the detected EoL sequence (default) @n
987 /// FALSE = report on the size of the EoL sequence to use when detecting an
989 const bool detected = true) noexcept {
990 return detected ? __eol_detected_size : __eol_standard_size;
991 } // -x- size_t size_eol -x-
993 /*======================================================================*//**
995 Provides the length of the line of text, including the EoL sequence (if one
997 @returns Number of bytes
1000 *///=========================================================================
1001 size_t size_with_eol() noexcept {
1002 return __line.size() + __eol_detected_size;
1003 } // -x- size_t size_with_eol -x-
1005 /*======================================================================*//**
1007 Converts the underlying std::string object to `std::vector<char>` without
1008 the EoL sequence, and returns it.
1009 @returns std::vector<char> (not null terminated)
1012 *///=========================================================================
1013 std::vector<char> to_vector() noexcept {
1014 return std::vector<char>(__line.data(), __line.data() + __line.size());
1015 } // -x- std::vector<char> to_vector -x-
1017 /*======================================================================*//**
1019 Convert this @c rline to an @c std::string without the EoL sequence.
1022 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1023 #include <string> // std::string
1024 #include <randolf/rline>
1026 int main(int argc, char *argv[]) {
1027 randolf::rline rl("This is an example.\n");
1029 std::cout << "\"" << s << "\"" << std::endl;
1030 return EXIT_SUCCESS;
1031 } // -x- int main -x-
1034 @returns The line of text as an std::string object
1036 *///=========================================================================
1037 operator std::string() const noexcept {
1039 } // -x- operator std::string -x-
1041 /*======================================================================*//**
1043 Convert this @c rline to an `std::vector<char>` without the EoL sequence.
1046 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1047 #include <string> // std::string
1048 #include <randolf/rline>
1050 int main(int argc, char *argv[]) {
1051 randolf::rline rl("This is an example.\n");
1052 std::vector<char> v = rl;
1053 std::cout << "\"" << v[0] << "\"" << std::endl;
1054 return EXIT_SUCCESS;
1055 } // -x- int main -x-
1058 @returns The line of text as an std::vector<char> object
1060 *///=========================================================================
1061 operator std::vector<char>() const noexcept {
1062 return std::vector<char>(__line.data(), __line.data() + __line.size());
1063 } // -x- operator std::vector<char> -x-
1065 /*======================================================================*//**
1067 Compare with the underlying std::string (without the EoL sequence).
1068 @returns TRUE = doesn't match@n
1070 *///=========================================================================
1072 /// String to compare with
1073 const std::string& s) {
1075 } // -x- bool operator== -x-
1077 /*======================================================================*//**
1079 Compare with the underlying std::string (without the EoL sequence).
1080 @returns TRUE = match@n
1081 FALSE = doesn't match
1082 *///=========================================================================
1084 /// String to compare with
1085 const std::string& s) {
1087 } // -x- bool operator== -x-
1089 /*======================================================================*//**
1091 Array-style access to the underlying std::string (without the EoL sequence).
1093 The first element is at index 0.
1094 @throws std::out_of_range if the index is out-of-range
1099 *///=========================================================================
1101 /// Index of character to access (0 = first element; negative index values
1102 /// are calculated in reverse, starting with -1 as the final position)
1105 // --------------------------------------------------------------------------
1106 // Internal variables.
1107 // --------------------------------------------------------------------------
1108 int MAX = __line.length();
1110 // --------------------------------------------------------------------------
1112 // --------------------------------------------------------------------------
1113 if (index >= MAX || 0 - index > MAX) throw std::out_of_range("index (position=" + std::to_string(index) + ") falls outside of the underlying string (length=" + std::to_string(MAX) + ")");
1115 return __line[index >= 0 ? index : MAX - index];
1116 } // -x- char operator[] -x-
1118 /*======================================================================*//**
1120 Support convenient streaming usage with std::cout, std::cerr, and friends.
1123 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1124 #include <string> // std::string
1125 #include <randolf/rline>
1127 int main(int argc, char *argv[]) {
1128 randolf::rline rl("This is an example.\n");
1129 std::cout << "\"" << rl << "\"" << std::endl;
1130 return EXIT_SUCCESS;
1131 } // -x- int main -x-
1134 @returns Line of text, without EoL sequence
1135 *///=========================================================================
1136 friend std::ostream& operator<< (
1137 /// Output stream (provided automatically by std::cout and std::cerr)
1139 /// Object class (matched by compiler)
1141 return o << &c.__line;
1142 } // -x- std::ostream& operator<< -x-
1144 }; // -x- class rline -x-
1146}; // -x- namespace randolf -x-