wordutils.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. /*
  2. Copyright 2014 Alexander Okoli
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. /*
  14. Package goutils provides utility functions to manipulate strings in various ways.
  15. The code snippets below show examples of how to use goutils. Some functions return
  16. errors while others do not, so usage would vary as a result.
  17. Example:
  18. package main
  19. import (
  20. "fmt"
  21. "github.com/aokoli/goutils"
  22. )
  23. func main() {
  24. // EXAMPLE 1: A goutils function which returns no errors
  25. fmt.Println (goutils.Initials("John Doe Foo")) // Prints out "JDF"
  26. // EXAMPLE 2: A goutils function which returns an error
  27. rand1, err1 := goutils.Random (-1, 0, 0, true, true)
  28. if err1 != nil {
  29. fmt.Println(err1) // Prints out error message because -1 was entered as the first parameter in goutils.Random(...)
  30. } else {
  31. fmt.Println(rand1)
  32. }
  33. }
  34. */
  35. package goutils
  36. import (
  37. "bytes"
  38. "strings"
  39. "unicode"
  40. )
  41. // VERSION indicates the current version of goutils
  42. const VERSION = "1.0.0"
  43. /*
  44. Wrap wraps a single line of text, identifying words by ' '.
  45. New lines will be separated by '\n'. Very long words, such as URLs will not be wrapped.
  46. Leading spaces on a new line are stripped. Trailing spaces are not stripped.
  47. Parameters:
  48. str - the string to be word wrapped
  49. wrapLength - the column (a column can fit only one character) to wrap the words at, less than 1 is treated as 1
  50. Returns:
  51. a line with newlines inserted
  52. */
  53. func Wrap(str string, wrapLength int) string {
  54. return WrapCustom(str, wrapLength, "", false)
  55. }
  56. /*
  57. WrapCustom wraps a single line of text, identifying words by ' '.
  58. Leading spaces on a new line are stripped. Trailing spaces are not stripped.
  59. Parameters:
  60. str - the string to be word wrapped
  61. wrapLength - the column number (a column can fit only one character) to wrap the words at, less than 1 is treated as 1
  62. newLineStr - the string to insert for a new line, "" uses '\n'
  63. wrapLongWords - true if long words (such as URLs) should be wrapped
  64. Returns:
  65. a line with newlines inserted
  66. */
  67. func WrapCustom(str string, wrapLength int, newLineStr string, wrapLongWords bool) string {
  68. if str == "" {
  69. return ""
  70. }
  71. if newLineStr == "" {
  72. newLineStr = "\n" // TODO Assumes "\n" is seperator. Explore SystemUtils.LINE_SEPARATOR from Apache Commons
  73. }
  74. if wrapLength < 1 {
  75. wrapLength = 1
  76. }
  77. inputLineLength := len(str)
  78. offset := 0
  79. var wrappedLine bytes.Buffer
  80. for inputLineLength-offset > wrapLength {
  81. if rune(str[offset]) == ' ' {
  82. offset++
  83. continue
  84. }
  85. end := wrapLength + offset + 1
  86. spaceToWrapAt := strings.LastIndex(str[offset:end], " ") + offset
  87. if spaceToWrapAt >= offset {
  88. // normal word (not longer than wrapLength)
  89. wrappedLine.WriteString(str[offset:spaceToWrapAt])
  90. wrappedLine.WriteString(newLineStr)
  91. offset = spaceToWrapAt + 1
  92. } else {
  93. // long word or URL
  94. if wrapLongWords {
  95. end := wrapLength + offset
  96. // long words are wrapped one line at a time
  97. wrappedLine.WriteString(str[offset:end])
  98. wrappedLine.WriteString(newLineStr)
  99. offset += wrapLength
  100. } else {
  101. // long words aren't wrapped, just extended beyond limit
  102. end := wrapLength + offset
  103. index := strings.IndexRune(str[end:len(str)], ' ')
  104. if index == -1 {
  105. wrappedLine.WriteString(str[offset:len(str)])
  106. offset = inputLineLength
  107. } else {
  108. spaceToWrapAt = index + end
  109. wrappedLine.WriteString(str[offset:spaceToWrapAt])
  110. wrappedLine.WriteString(newLineStr)
  111. offset = spaceToWrapAt + 1
  112. }
  113. }
  114. }
  115. }
  116. wrappedLine.WriteString(str[offset:len(str)])
  117. return wrappedLine.String()
  118. }
  119. /*
  120. Capitalize capitalizes all the delimiter separated words in a string. Only the first letter of each word is changed.
  121. To convert the rest of each word to lowercase at the same time, use CapitalizeFully(str string, delimiters ...rune).
  122. The delimiters represent a set of characters understood to separate words. The first string character
  123. and the first non-delimiter character after a delimiter will be capitalized. A "" input string returns "".
  124. Capitalization uses the Unicode title case, normally equivalent to upper case.
  125. Parameters:
  126. str - the string to capitalize
  127. delimiters - set of characters to determine capitalization, exclusion of this parameter means whitespace would be delimeter
  128. Returns:
  129. capitalized string
  130. */
  131. func Capitalize(str string, delimiters ...rune) string {
  132. var delimLen int
  133. if delimiters == nil {
  134. delimLen = -1
  135. } else {
  136. delimLen = len(delimiters)
  137. }
  138. if str == "" || delimLen == 0 {
  139. return str
  140. }
  141. buffer := []rune(str)
  142. capitalizeNext := true
  143. for i := 0; i < len(buffer); i++ {
  144. ch := buffer[i]
  145. if isDelimiter(ch, delimiters...) {
  146. capitalizeNext = true
  147. } else if capitalizeNext {
  148. buffer[i] = unicode.ToTitle(ch)
  149. capitalizeNext = false
  150. }
  151. }
  152. return string(buffer)
  153. }
  154. /*
  155. CapitalizeFully converts all the delimiter separated words in a string into capitalized words, that is each word is made up of a
  156. titlecase character and then a series of lowercase characters. The delimiters represent a set of characters understood
  157. to separate words. The first string character and the first non-delimiter character after a delimiter will be capitalized.
  158. Capitalization uses the Unicode title case, normally equivalent to upper case.
  159. Parameters:
  160. str - the string to capitalize fully
  161. delimiters - set of characters to determine capitalization, exclusion of this parameter means whitespace would be delimeter
  162. Returns:
  163. capitalized string
  164. */
  165. func CapitalizeFully(str string, delimiters ...rune) string {
  166. var delimLen int
  167. if delimiters == nil {
  168. delimLen = -1
  169. } else {
  170. delimLen = len(delimiters)
  171. }
  172. if str == "" || delimLen == 0 {
  173. return str
  174. }
  175. str = strings.ToLower(str)
  176. return Capitalize(str, delimiters...)
  177. }
  178. /*
  179. Uncapitalize uncapitalizes all the whitespace separated words in a string. Only the first letter of each word is changed.
  180. The delimiters represent a set of characters understood to separate words. The first string character and the first non-delimiter
  181. character after a delimiter will be uncapitalized. Whitespace is defined by unicode.IsSpace(char).
  182. Parameters:
  183. str - the string to uncapitalize fully
  184. delimiters - set of characters to determine capitalization, exclusion of this parameter means whitespace would be delimeter
  185. Returns:
  186. uncapitalized string
  187. */
  188. func Uncapitalize(str string, delimiters ...rune) string {
  189. var delimLen int
  190. if delimiters == nil {
  191. delimLen = -1
  192. } else {
  193. delimLen = len(delimiters)
  194. }
  195. if str == "" || delimLen == 0 {
  196. return str
  197. }
  198. buffer := []rune(str)
  199. uncapitalizeNext := true // TODO Always makes capitalize/un apply to first char.
  200. for i := 0; i < len(buffer); i++ {
  201. ch := buffer[i]
  202. if isDelimiter(ch, delimiters...) {
  203. uncapitalizeNext = true
  204. } else if uncapitalizeNext {
  205. buffer[i] = unicode.ToLower(ch)
  206. uncapitalizeNext = false
  207. }
  208. }
  209. return string(buffer)
  210. }
  211. /*
  212. SwapCase swaps the case of a string using a word based algorithm.
  213. Conversion algorithm:
  214. Upper case character converts to Lower case
  215. Title case character converts to Lower case
  216. Lower case character after Whitespace or at start converts to Title case
  217. Other Lower case character converts to Upper case
  218. Whitespace is defined by unicode.IsSpace(char).
  219. Parameters:
  220. str - the string to swap case
  221. Returns:
  222. the changed string
  223. */
  224. func SwapCase(str string) string {
  225. if str == "" {
  226. return str
  227. }
  228. buffer := []rune(str)
  229. whitespace := true
  230. for i := 0; i < len(buffer); i++ {
  231. ch := buffer[i]
  232. if unicode.IsUpper(ch) {
  233. buffer[i] = unicode.ToLower(ch)
  234. whitespace = false
  235. } else if unicode.IsTitle(ch) {
  236. buffer[i] = unicode.ToLower(ch)
  237. whitespace = false
  238. } else if unicode.IsLower(ch) {
  239. if whitespace {
  240. buffer[i] = unicode.ToTitle(ch)
  241. whitespace = false
  242. } else {
  243. buffer[i] = unicode.ToUpper(ch)
  244. }
  245. } else {
  246. whitespace = unicode.IsSpace(ch)
  247. }
  248. }
  249. return string(buffer)
  250. }
  251. /*
  252. Initials extracts the initial letters from each word in the string. The first letter of the string and all first
  253. letters after the defined delimiters are returned as a new string. Their case is not changed. If the delimiters
  254. parameter is excluded, then Whitespace is used. Whitespace is defined by unicode.IsSpacea(char). An empty delimiter array returns an empty string.
  255. Parameters:
  256. str - the string to get initials from
  257. delimiters - set of characters to determine words, exclusion of this parameter means whitespace would be delimeter
  258. Returns:
  259. string of initial letters
  260. */
  261. func Initials(str string, delimiters ...rune) string {
  262. if str == "" {
  263. return str
  264. }
  265. if delimiters != nil && len(delimiters) == 0 {
  266. return ""
  267. }
  268. strLen := len(str)
  269. var buf bytes.Buffer
  270. lastWasGap := true
  271. for i := 0; i < strLen; i++ {
  272. ch := rune(str[i])
  273. if isDelimiter(ch, delimiters...) {
  274. lastWasGap = true
  275. } else if lastWasGap {
  276. buf.WriteRune(ch)
  277. lastWasGap = false
  278. }
  279. }
  280. return buf.String()
  281. }
  282. // private function (lower case func name)
  283. func isDelimiter(ch rune, delimiters ...rune) bool {
  284. if delimiters == nil {
  285. return unicode.IsSpace(ch)
  286. }
  287. for _, delimiter := range delimiters {
  288. if ch == delimiter {
  289. return true
  290. }
  291. }
  292. return false
  293. }