1 // Copyright 2011 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_I18N_BREAK_ITERATOR_H_ 6 #define BASE_I18N_BREAK_ITERATOR_H_ 7 8 #include <stddef.h> 9 10 #include <memory> 11 #include <string> 12 13 #include "base/i18n/base_i18n_export.h" 14 #include "base/memory/raw_ptr.h" 15 #include "base/strings/string_piece.h" 16 17 // The BreakIterator class iterates through the words, word breaks, and 18 // line breaks in a UTF-16 string. 19 // 20 // It provides several modes, BREAK_WORD, BREAK_LINE, BREAK_NEWLINE, and 21 // BREAK_SENTENCE which modify how characters are aggregated into the returned 22 // string. 23 // 24 // Under BREAK_WORD mode, once a word is encountered any non-word 25 // characters are not included in the returned string (e.g. in the 26 // UTF-16 equivalent of the string " foo bar! ", the word breaks are at 27 // the periods in ". .foo. .bar.!. ."). 28 // Note that Chinese/Japanese/Thai do not use spaces between words so that 29 // boundaries can fall in the middle of a continuous run of non-space / 30 // non-punctuation characters. 31 // 32 // Under BREAK_LINE mode, once a line breaking opportunity is encountered, 33 // any non-word characters are included in the returned string, breaking 34 // only when a space-equivalent character or a line breaking opportunity 35 // is encountered (e.g. in the UTF16-equivalent of the string " foo bar! ", 36 // the breaks are at the periods in ". .foo .bar! ."). 37 // 38 // Note that lines can be broken at any character/syllable/grapheme cluster 39 // boundary in Chinese/Japanese/Korean and at word boundaries in Thai 40 // (Thai does not use spaces between words). Therefore, this is NOT the same 41 // as breaking only at space-equivalent characters where its former 42 // name (BREAK_SPACE) implied. 43 // 44 // Under BREAK_NEWLINE mode, all characters are included in the returned 45 // string, breaking only when a newline-equivalent character is encountered 46 // (eg. in the UTF-16 equivalent of the string "foo\nbar!\n\n", the line 47 // breaks are at the periods in ".foo\n.bar\n.\n."). 48 // 49 // Under BREAK_SENTENCE mode, all characters are included in the returned 50 // string, breaking only on sentence boundaries defined in "Unicode Standard 51 // Annex #29: Text Segmentation." Whitespace immediately following the sentence 52 // is also included. For example, in the UTF-16 equivalent of the string 53 // "foo bar! baz qux?" the breaks are at the periods in ".foo bar! .baz quz?." 54 // 55 // To extract the words from a string, move a BREAK_WORD BreakIterator 56 // through the string and test whether IsWord() is true. E.g., 57 // BreakIterator iter(str, BreakIterator::BREAK_WORD); 58 // if (!iter.Init()) 59 // return false; 60 // while (iter.Advance()) { 61 // if (iter.IsWord()) { 62 // // Region [iter.prev(), iter.pos()) contains a word. 63 // VLOG(1) << "word: " << iter.GetString(); 64 // } 65 // } 66 67 // ICU iterator type. It is forward declared to avoid including transitively the 68 // full ICU headers toward every dependent files. 69 struct UBreakIterator; 70 71 namespace base { 72 namespace i18n { 73 74 struct UBreakIteratorDeleter { 75 void operator()(UBreakIterator*); 76 }; 77 using UBreakIteratorPtr = 78 std::unique_ptr<UBreakIterator, UBreakIteratorDeleter>; 79 80 class BASE_I18N_EXPORT BreakIterator { 81 public: 82 enum BreakType { 83 BREAK_WORD, 84 BREAK_LINE, 85 // TODO(jshin): Remove this after reviewing call sites. 86 // If call sites really need break only on space-like characters 87 // implement it separately. 88 BREAK_SPACE = BREAK_LINE, 89 BREAK_NEWLINE, 90 BREAK_CHARACTER, 91 // But don't remove this one! 92 RULE_BASED, 93 BREAK_SENTENCE, 94 }; 95 96 enum WordBreakStatus { 97 // The end of text that the iterator recognizes as word characters. 98 // Non-word characters are things like punctuation and spaces. 99 IS_WORD_BREAK, 100 // Characters that the iterator can skip past, such as punctuation, 101 // whitespace, and, if using RULE_BASED mode, characters from another 102 // character set. 103 IS_SKIPPABLE_WORD, 104 // Only used if not in BREAK_WORD or RULE_BASED mode. This is returned for 105 // newlines, line breaks, and character breaks. 106 IS_LINE_OR_CHAR_BREAK 107 }; 108 109 static constexpr size_t npos = static_cast<size_t>(-1); 110 111 // Requires |str| to live as long as the BreakIterator does. 112 BreakIterator(StringPiece16 str, BreakType break_type); 113 // Make a rule-based iterator. BreakType == RULE_BASED is implied. 114 // TODO(andrewhayden): This signature could easily be misinterpreted as 115 // "(const std::u16string& str, const std::u16string& locale)". We should do 116 // something better. 117 BreakIterator(StringPiece16 str, const std::u16string& rules); 118 119 BreakIterator(const BreakIterator&) = delete; 120 BreakIterator& operator=(const BreakIterator&) = delete; 121 122 ~BreakIterator(); 123 124 // Init() must be called before any of the iterators are valid. 125 // Returns false if ICU failed to initialize. 126 bool Init(); 127 128 // Advance to the next break. Returns false if we've run past the end of 129 // the string. (Note that the very last "break" is after the final 130 // character in the string, and when we advance to that position it's the 131 // last time Advance() returns true.) 132 bool Advance(); 133 134 // Updates the text used by the iterator, resetting the iterator as if 135 // if Init() had been called again. Any old state is lost. Returns true 136 // unless there is an error setting the text. 137 bool SetText(const char16_t* text, const size_t length); 138 139 // Under BREAK_WORD mode, returns true if the break we just hit is the 140 // end of a word. (Otherwise, the break iterator just skipped over e.g. 141 // whitespace or punctuation.) Under BREAK_LINE and BREAK_NEWLINE modes, 142 // this distinction doesn't apply and it always returns false. 143 bool IsWord() const; 144 145 // Under BREAK_WORD mode: 146 // - Returns IS_SKIPPABLE_WORD if non-word characters, such as punctuation or 147 // spaces, are found. 148 // - Returns IS_WORD_BREAK if the break we just hit is the end of a sequence 149 // of word characters. 150 // Under RULE_BASED mode: 151 // - Returns IS_SKIPPABLE_WORD if characters outside the rules' character set 152 // or non-word characters, such as punctuation or spaces, are found. 153 // - Returns IS_WORD_BREAK if the break we just hit is the end of a sequence 154 // of word characters that are in the rules' character set. 155 // Not under BREAK_WORD or RULE_BASED mode: 156 // - Returns IS_LINE_OR_CHAR_BREAK. 157 BreakIterator::WordBreakStatus GetWordBreakStatus() const; 158 159 // Under BREAK_WORD mode, returns true if |position| is at the end of word or 160 // at the start of word. It always returns false under modes that are not 161 // BREAK_WORD or RULE_BASED. 162 bool IsEndOfWord(size_t position) const; 163 bool IsStartOfWord(size_t position) const; 164 165 // Under BREAK_SENTENCE mode, returns true if |position| is at a sentence 166 // boundary. It always returns false under modes that are not BREAK_SENTENCE 167 // or RULE_BASED. 168 bool IsSentenceBoundary(size_t position) const; 169 170 // Under BREAK_CHARACTER mode, returns whether |position| is a Unicode 171 // grapheme boundary. 172 bool IsGraphemeBoundary(size_t position) const; 173 174 // Returns the string between prev() and pos(). 175 // Advance() must have been called successfully at least once for pos() to 176 // have advanced to somewhere useful. 177 std::u16string GetString() const; 178 179 StringPiece16 GetStringPiece() const; 180 181 // Returns the value of pos() returned before Advance() was last called. prev()182 size_t prev() const { return prev_; } 183 184 // Returns the current break position within the string, 185 // or BreakIterator::npos when done. pos()186 size_t pos() const { return pos_; } 187 188 private: 189 UBreakIteratorPtr iter_; 190 191 // The string we're iterating over. Can be changed with SetText(...) 192 StringPiece16 string_; 193 194 // Rules for our iterator. Mutually exclusive with break_type_. 195 const std::u16string rules_; 196 197 // The breaking style (word/space/newline). Mutually exclusive with rules_ 198 const BreakType break_type_; 199 200 // Previous and current iterator positions. 201 size_t prev_ = npos; 202 size_t pos_ = 0; 203 }; 204 205 } // namespace i18n 206 } // namespace base 207 208 #endif // BASE_I18N_BREAK_ITERATOR_H_ 209