format.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // Copyright 2015 Huan Du. All rights reserved.
  2. // Licensed under the MIT license that can be found in the LICENSE file.
  3. package xstrings
  4. import (
  5. "unicode/utf8"
  6. )
  7. // ExpandTabs can expand tabs ('\t') rune in str to one or more spaces dpending on
  8. // current column and tabSize.
  9. // The column number is reset to zero after each newline ('\n') occurring in the str.
  10. //
  11. // ExpandTabs uses RuneWidth to decide rune's width.
  12. // For example, CJK characters will be treated as two characters.
  13. //
  14. // If tabSize <= 0, ExpandTabs panics with error.
  15. //
  16. // Samples:
  17. // ExpandTabs("a\tbc\tdef\tghij\tk", 4) => "a bc def ghij k"
  18. // ExpandTabs("abcdefg\thij\nk\tl", 4) => "abcdefg hij\nk l"
  19. // ExpandTabs("z中\t文\tw", 4) => "z中 文 w"
  20. func ExpandTabs(str string, tabSize int) string {
  21. if tabSize <= 0 {
  22. panic("tab size must be positive")
  23. }
  24. var r rune
  25. var i, size, column, expand int
  26. var output *stringBuilder
  27. orig := str
  28. for len(str) > 0 {
  29. r, size = utf8.DecodeRuneInString(str)
  30. if r == '\t' {
  31. expand = tabSize - column%tabSize
  32. if output == nil {
  33. output = allocBuffer(orig, str)
  34. }
  35. for i = 0; i < expand; i++ {
  36. output.WriteRune(' ')
  37. }
  38. column += expand
  39. } else {
  40. if r == '\n' {
  41. column = 0
  42. } else {
  43. column += RuneWidth(r)
  44. }
  45. if output != nil {
  46. output.WriteRune(r)
  47. }
  48. }
  49. str = str[size:]
  50. }
  51. if output == nil {
  52. return orig
  53. }
  54. return output.String()
  55. }
  56. // LeftJustify returns a string with pad string at right side if str's rune length is smaller than length.
  57. // If str's rune length is larger than length, str itself will be returned.
  58. //
  59. // If pad is an empty string, str will be returned.
  60. //
  61. // Samples:
  62. // LeftJustify("hello", 4, " ") => "hello"
  63. // LeftJustify("hello", 10, " ") => "hello "
  64. // LeftJustify("hello", 10, "123") => "hello12312"
  65. func LeftJustify(str string, length int, pad string) string {
  66. l := Len(str)
  67. if l >= length || pad == "" {
  68. return str
  69. }
  70. remains := length - l
  71. padLen := Len(pad)
  72. output := &stringBuilder{}
  73. output.Grow(len(str) + (remains/padLen+1)*len(pad))
  74. output.WriteString(str)
  75. writePadString(output, pad, padLen, remains)
  76. return output.String()
  77. }
  78. // RightJustify returns a string with pad string at left side if str's rune length is smaller than length.
  79. // If str's rune length is larger than length, str itself will be returned.
  80. //
  81. // If pad is an empty string, str will be returned.
  82. //
  83. // Samples:
  84. // RightJustify("hello", 4, " ") => "hello"
  85. // RightJustify("hello", 10, " ") => " hello"
  86. // RightJustify("hello", 10, "123") => "12312hello"
  87. func RightJustify(str string, length int, pad string) string {
  88. l := Len(str)
  89. if l >= length || pad == "" {
  90. return str
  91. }
  92. remains := length - l
  93. padLen := Len(pad)
  94. output := &stringBuilder{}
  95. output.Grow(len(str) + (remains/padLen+1)*len(pad))
  96. writePadString(output, pad, padLen, remains)
  97. output.WriteString(str)
  98. return output.String()
  99. }
  100. // Center returns a string with pad string at both side if str's rune length is smaller than length.
  101. // If str's rune length is larger than length, str itself will be returned.
  102. //
  103. // If pad is an empty string, str will be returned.
  104. //
  105. // Samples:
  106. // Center("hello", 4, " ") => "hello"
  107. // Center("hello", 10, " ") => " hello "
  108. // Center("hello", 10, "123") => "12hello123"
  109. func Center(str string, length int, pad string) string {
  110. l := Len(str)
  111. if l >= length || pad == "" {
  112. return str
  113. }
  114. remains := length - l
  115. padLen := Len(pad)
  116. output := &stringBuilder{}
  117. output.Grow(len(str) + (remains/padLen+1)*len(pad))
  118. writePadString(output, pad, padLen, remains/2)
  119. output.WriteString(str)
  120. writePadString(output, pad, padLen, (remains+1)/2)
  121. return output.String()
  122. }
  123. func writePadString(output *stringBuilder, pad string, padLen, remains int) {
  124. var r rune
  125. var size int
  126. repeats := remains / padLen
  127. for i := 0; i < repeats; i++ {
  128. output.WriteString(pad)
  129. }
  130. remains = remains % padLen
  131. if remains != 0 {
  132. for i := 0; i < remains; i++ {
  133. r, size = utf8.DecodeRuneInString(pad)
  134. output.WriteRune(r)
  135. pad = pad[size:]
  136. }
  137. }
  138. }