PageRenderTime 108ms CodeModel.GetById 21ms RepoModel.GetById 2ms app.codeStats 0ms

/lib/hash-utils/string.rb

https://github.com/yb66/hash-utils
Ruby | 906 lines | 368 code | 128 blank | 410 comment | 74 complexity | a766cf7fad380b1981e54f50e9dd88cc MD5 | raw file
  1. # encoding: utf-8
  2. # (c) 2011-2012 Martin Kozรกk (martinkozak@martinkozak.net)
  3. require "ruby-version"
  4. require "hash-utils/array"
  5. require "hash-utils/hash"
  6. require "hash-utils/object"
  7. ##
  8. # String extension.
  9. #
  10. class String
  11. ##
  12. # Holds numeric matcher.
  13. # @since 0.3.0
  14. #
  15. if not self.constants.include? :NUMERIC
  16. NUMERIC = /^\s*-?\d+(?:\.\d+)?\s*$/
  17. end
  18. ##
  19. # Holds character interlacing matcher.
  20. # @since 0.18.1
  21. #
  22. if not self.constants.include? :INTERLACING
  23. INTERLACING = /(.)([^$])/
  24. end
  25. ##
  26. # Holds lower case alpha characters for random string generator.
  27. # @since 0.19.0
  28. #
  29. if not self.constants.include? :RANDOM_ALPHA_LOWER
  30. RANDOM_ALPHA_LOWER = "abcdefghijklmnopqrstuvwxyz"
  31. end
  32. ##
  33. # Holds upper case alpha characters for random string generator.
  34. # @since 0.19.0
  35. #
  36. if not self.constants.include? :RANDOM_ALPHA_UPPER
  37. RANDOM_ALPHA_UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  38. end
  39. ##
  40. # Holds number characters list for random string generator.
  41. # @since 0.19.0
  42. #
  43. if not self.constants.include? :RANDOM_NUMBERS
  44. RANDOM_NUMBERS = "0123456789"
  45. end
  46. ##
  47. # Holds symbols list for random string generator.
  48. # @since 0.19.0
  49. #
  50. if not self.constants.include? :RANDOM_SYMBOLS
  51. RANDOM_SYMBOLS = "!\/#?.,_-+*@%&{}[]()=<>|~'$"
  52. end
  53. ##
  54. # Holds whitespace for random string generator.
  55. # @since 0.19.0
  56. #
  57. if not self.constants.include? :RANDOM_WHITESPACE
  58. RANDOM_WHITESPACE = " "
  59. end
  60. ##
  61. # Holds full spectrum of possible characters in random string generator.
  62. # @since 0.19.0
  63. #
  64. if not self.constants.include? :RANDOM_FULL
  65. RANDOM_FULL = self::RANDOM_ALPHA_LOWER + self::RANDOM_ALPHA_UPPER + self::RANDOM_NUMBERS + self::RANDOM_SYMBOLS + self::RANDOM_WHITESPACE
  66. end
  67. ##
  68. # Returns random string.
  69. #
  70. # @param [Integer] length length of the required string
  71. # @param [String] characters list of the characters
  72. # @return [String] new random string
  73. # @since 0.19.0
  74. #
  75. if not self.respond_to? :random
  76. def self.random(length, characters = self::RANDOM_FULL)
  77. result = ""
  78. max = characters.length
  79. length.times do
  80. result << characters[Kernel.rand(max)].chr
  81. end
  82. return result
  83. end
  84. end
  85. ##
  86. # Indicates, string is numeric, so consists of numbers only.
  87. #
  88. # @return [Boolean] +true+ if yes, +false+ in otherwise
  89. # @since 0.3.0
  90. #
  91. if not self.__hash_utils_instance_respond_to? :numeric?
  92. def numeric?
  93. if self.match(self.class::NUMERIC)
  94. true
  95. else
  96. false
  97. end
  98. end
  99. end
  100. ##
  101. # Replaces set of substrings by another strings.
  102. #
  103. # It's equivalent of PHP +strtr()+ function. Supports all objects
  104. # convertable to String, but in that case you must give him block
  105. # which specifies how to map keys found in the string back to
  106. # keys in definition Hash or Array.
  107. #
  108. # If you specify the +:flat+ mode, definition array items will be
  109. # treaten as arguments to +Hash#[]+ method while internal conversion
  110. # +Array+ to +Hash+, so then can then use plain array as base for
  111. # definitions. See {Array#to_h}.
  112. #
  113. # @example Equivalent calls
  114. # "aa bb".strtr("aa" => "bb", "bb" => "aa")
  115. # "aa bb".strtr([["aa", "bb"], ["bb", "aa"]])
  116. # "aa bb".strtr(["aa", "bb", "bb", "aa"], :flat)
  117. # @example Use with symbols
  118. # "aa bb".strtr(:aa => "bb", :bb => "aa") { |s| s.to_sym }
  119. #
  120. # @param [Array, Hash] replacements replacements definition
  121. # @param [Symbol] mode flat mode switch, can be +:flat+ or +nil+
  122. # @param [Proc] block with keys mapping worker (see description)
  123. # @return [String] string with applied replacements
  124. # @see http://www.php.net/strtr
  125. # @since 0.4.0
  126. #
  127. if not self.__hash_utils_instance_respond_to? :strtr
  128. def strtr(defs, mode = nil, &block)
  129. if block.nil?
  130. block = Proc::new { |s| s }
  131. end
  132. defs, matcher = __prepare_strtr(defs, mode)
  133. self.gsub(matcher) { |s| defs[block.call(s)] }
  134. end
  135. end
  136. ##
  137. # Replaces set of substrings by another strings -- in place.
  138. # See {#strtr} for details.
  139. #
  140. # @param [Array, Hash] replacements replacements definition
  141. # @param [Symbol] mode flat mode switch, can be +:flat+ or +nil+
  142. # @param [Proc] block with keys mapping worker (see description)
  143. # @return [String] string with applied replacements
  144. # @see #strtr
  145. # @since 0.4.0
  146. #
  147. if not self.__hash_utils_instance_respond_to? :strtr!
  148. def strtr!(defs, mode = nil, &block)
  149. if block.nil?
  150. block = Proc::new { |s| s }
  151. end
  152. defs, matcher = __prepare_strtr(defs, mode)
  153. self.gsub!(matcher) { |s| defs[block.call(s)] }
  154. end
  155. end
  156. ##
  157. # Converts String to Array of characters.
  158. #
  159. # @example
  160. # foo = "012"
  161. # puts foo.to_a.inspect # prints out ["0", "1", "2"]
  162. #
  163. # @param String glue glue according to convert to array
  164. # @return [Array] array of single character strings
  165. # @since 0.6.0
  166. #
  167. if not self.__hash_utils_instance_respond_to? :to_a
  168. def to_a(glue = "")
  169. self.split(glue)
  170. end
  171. end
  172. ##
  173. # Applies block to each character and returns resultant string.
  174. #
  175. # @example
  176. # foo = "012"
  177. # puts foo.map { |ch| (ch.to_i + 1).to_s }.inspect # prints out "123"
  178. #
  179. # @param [Proc] block transforming block
  180. # @param [String] transformed string
  181. # @since 0.6.0
  182. #
  183. if not self.__hash_utils_instance_respond_to? :map
  184. if Ruby::Version >= "1.9"
  185. def map(&block)
  186. buffer = " " * self.length
  187. self.length.times do |i|
  188. buffer[i] = block.call(self[i]).to_s
  189. end
  190. return buffer
  191. end
  192. else
  193. def map(&block)
  194. buffer = " " * self.length
  195. self.length.times do |i|
  196. buffer[i] = block.call(self[i]).to_i
  197. end
  198. return buffer
  199. end
  200. end
  201. end
  202. ##
  203. # Applies block to each character in place. For example see {#map}.
  204. #
  205. # @param [Proc] block transforming block
  206. # @return [String] self
  207. # @since 0.6.0
  208. # @see #map
  209. #
  210. if not self.__hash_utils_instance_respond_to? :map!
  211. if Ruby::Version >= "1.9"
  212. def map!(&block)
  213. self.length.times do |i|
  214. self[i] = block.call(self[i]).to_s
  215. end
  216. return self
  217. end
  218. else
  219. def map!(&block)
  220. self.length.times do |i|
  221. self[i] = block.call(self[i]).to_i
  222. end
  223. return self
  224. end
  225. end
  226. end
  227. ##
  228. # Replaces all substrings defined by Regexp by complex way. It
  229. # means, it gives to block not only whole match, but submatches too.
  230. # In fact, emulates PHP's +preg_replace_callback()+ function.
  231. # In other ways emulates standard +#gsub+.
  232. #
  233. # @param [Regexp] regexp matching expression
  234. # @param [String] to new string
  235. # @param [Proc] block block which will receive each match
  236. # @return [String] resultant string
  237. # @see http://www.php.net/preg_replace_callback
  238. # @since 0.8.0
  239. #
  240. if not self.__hash_utils_instance_respond_to? :gsub_f
  241. def gsub_f(from, to = nil, &block)
  242. __prepare_gsub_f(from, to, block) do |callback|
  243. if to.nil?
  244. self.gsub(from, &callback)
  245. else
  246. self.gsub(from, to)
  247. end
  248. end
  249. end
  250. end
  251. ##
  252. # Performs complex regexp replacing same as {#gsub_f}, but in place.
  253. # In other ways emulates standard +#gsub!+.
  254. #
  255. # @param [Regexp] from matching expression
  256. # @param [String] to new string
  257. # @param [Proc] block block which will receive each match
  258. # @return [String] resultant string
  259. # @see #gsub_f
  260. # @since 0.8.0
  261. #
  262. if not self.__hash_utils_instance_respond_to? :gsub_f!
  263. def gsub_f!(from, to = nil, &block)
  264. __prepare_gsub_f(from, to, block) do |callback|
  265. if to.nil?
  266. self.gsub!(from, &callback)
  267. else
  268. self.gsub!(from, to)
  269. end
  270. end
  271. return self
  272. end
  273. end
  274. ##
  275. # Returns first character of the string.
  276. #
  277. # @example
  278. # "abc".first # will return 'a'
  279. #
  280. # @return [String] first character
  281. # @since 0.11.0
  282. #
  283. if not self.__hash_utils_instance_respond_to? :first
  284. def first
  285. self[0].chr
  286. end
  287. end
  288. ##
  289. # Returns second character of the string.
  290. #
  291. # @return [String] second character
  292. # @since 0.15.0
  293. #
  294. if not self.__hash_utils_instance_respond_to? :second
  295. def second
  296. self[1].chr
  297. end
  298. end
  299. ##
  300. # Returns third character of the string.
  301. #
  302. # @return [String] third character
  303. # @since 0.15.0
  304. #
  305. if not self.__hash_utils_instance_respond_to? :third
  306. def third
  307. self[2].chr
  308. end
  309. end
  310. ##
  311. # Returns fourth character of the string.
  312. #
  313. # @return [String] fourth character
  314. # @since 0.15.0
  315. #
  316. if not self.__hash_utils_instance_respond_to? :fourth
  317. def fourth
  318. self[3].chr
  319. end
  320. end
  321. ##
  322. # Returns fifth character of the string.
  323. #
  324. # @return [String] fifth character
  325. # @since 0.15.0
  326. #
  327. if not self.__hash_utils_instance_respond_to? :fifth
  328. def fifth
  329. self[4].chr
  330. end
  331. end
  332. ##
  333. # Returns sixth character of the string.
  334. #
  335. # @return [String] sixth character
  336. # @since 0.15.0
  337. #
  338. if not self.__hash_utils_instance_respond_to? :sixth
  339. def sixth
  340. self[5].chr
  341. end
  342. end
  343. ##
  344. # Returns seventh character of the string.
  345. #
  346. # @return [String] seventh character
  347. # @since 0.15.0
  348. #
  349. if not self.__hash_utils_instance_respond_to? :seventh
  350. def seventh
  351. self[6].chr
  352. end
  353. end
  354. ##
  355. # Returns eighth character of the string.
  356. #
  357. # @return [String] eighth character
  358. # @since 0.15.0
  359. #
  360. if not self.__hash_utils_instance_respond_to? :eighth
  361. def eighth
  362. self[7].chr
  363. end
  364. end
  365. ##
  366. # Returns last character of the string.
  367. #
  368. # @example
  369. # "abc".last # will return 'c'
  370. #
  371. # @return [String] last character
  372. # @since 0.11.0
  373. #
  374. if not self.__hash_utils_instance_respond_to? :last
  375. def last
  376. self[-1].chr
  377. end
  378. end
  379. ##
  380. # Returns required count of lines from beginning of string.
  381. #
  382. # @example
  383. # "a\nb\nc\nd\n".first_lines(2)
  384. # # will return ["a\n", "b\n"]
  385. #
  386. # @note Works well for UNIX strings only. Convert your string
  387. # if necessary.
  388. # @param [Integer] count required count of lines
  389. # @return [Array] array of lines
  390. # @since 0.12.0
  391. #
  392. if not self.__hash_utils_instance_respond_to? :first_lines
  393. def first_lines(count = 1)
  394. result = [ ]
  395. self.each_line do |line|
  396. count -= 1
  397. result << line
  398. break if count == 0
  399. end
  400. return result
  401. end
  402. end
  403. ##
  404. # Returns first line of the string.
  405. #
  406. # @example
  407. # "a\nb\nc\nd\n".first_line
  408. # # will return "a\n"
  409. #
  410. # @note Works well for UNIX strings only. Convert your string
  411. # if necessary.
  412. # @return [String] line with +\n+
  413. # @since 0.12.0
  414. #
  415. if not self.__hash_utils_instance_respond_to? :first_line
  416. def first_line
  417. self.first_lines.first
  418. end
  419. end
  420. ##
  421. # Returns required count of lines from end of string.
  422. #
  423. # @example
  424. # "a\nb\nc\nd".last_lines(2)
  425. # # will return ["c\n", "d"]
  426. #
  427. # @note Works well for UNIX strings only. Convert your string
  428. # if necessary.
  429. # @param [Integer] count required count of lines
  430. # @return [Array] array of lines
  431. # @since 0.12.0
  432. #
  433. if not self.__hash_utils_instance_respond_to? :last_lines
  434. def last_lines(count = 1)
  435. buffer = ""
  436. result = [ ]
  437. (self.length - 1).downto(0) do |i|
  438. chr = self[i]
  439. if chr.ord == 10
  440. count -= 1
  441. result << buffer.reverse!
  442. buffer = ""
  443. break if count == 0
  444. end
  445. buffer << chr.chr
  446. end
  447. if count != 0
  448. result << buffer.reverse!
  449. end
  450. return result.reverse!
  451. end
  452. end
  453. ##
  454. # Returns last line of the string.
  455. #
  456. # @example
  457. # "a\nb\nc\nd".last_line
  458. # # will return "d"
  459. #
  460. # @note Works well for UNIX strings only. Convert your string
  461. # if necessary.
  462. # @return [String] line
  463. # @since 0.12.0
  464. #
  465. if not self.__hash_utils_instance_respond_to? :last_line
  466. def last_line
  467. self.last_lines.last
  468. end
  469. end
  470. ##
  471. # Removes given count of lines from beginning of file.
  472. #
  473. # @example
  474. # str = "a\nb\nc\nd"
  475. #
  476. # str.shift_lines(2)
  477. # # will return ["a\n", "b\n"]
  478. # p str
  479. # # will print out "c\nd"
  480. #
  481. # @note Works well for UNIX strings only. Convert your string
  482. # if necessary.
  483. # @param [Integer] count required number of lines
  484. # @return [Array] removed lines
  485. # @since 0.12.0
  486. #
  487. if not self.__hash_utils_instance_respond_to? :shift_lines
  488. def shift_lines(count = 1)
  489. lines = self.first_lines(count)
  490. length = lines.reduce(0) { |sum, i| sum + i.length }
  491. self.replace(self[length..-1])
  492. return lines
  493. end
  494. end
  495. ##
  496. # Removes first line out from the string and returns it.
  497. #
  498. # @return [String] removed line
  499. # @since 0.12.0
  500. #
  501. if not self.__hash_utils_instance_respond_to? :shift_line
  502. def shift_line
  503. self.shift_lines.first
  504. end
  505. end
  506. ##
  507. # Puts lines to begin of string.
  508. #
  509. # @param [Array] lines line bodies without +\n+
  510. # @return [String] itself
  511. # @since 0.12.0
  512. #
  513. if not self.__hash_utils_instance_respond_to? :unshift_lines
  514. def unshift_lines(*lines)
  515. self.unshift(lines.join("\n") << "\n")
  516. end
  517. end
  518. if not self.__hash_utils_instance_respond_to? :unshift_line
  519. alias :unshift_line :unshift_lines
  520. end
  521. ##
  522. # Removes lines out of end of the string.
  523. #
  524. # @note Works well for UNIX strings only. Convert your string
  525. # if necessary.
  526. # @param [Integer] count required number of lines
  527. # @return [Array] removed lines
  528. # @since 0.12.0
  529. #
  530. if not self.__hash_utils_instance_respond_to? :pop_lines
  531. def pop_lines(count = 1)
  532. lines = self.last_lines(count)
  533. length = lines.inject(0) { |sum, i| sum + i.length }
  534. self.replace(self[0..(length - 1)])
  535. return lines
  536. end
  537. end
  538. ##
  539. # Removes last line out from the string and returns it.
  540. # @return [String] removed line
  541. #
  542. if not self.__hash_utils_instance_respond_to? :pop_line
  543. def pop_line
  544. self.pop_lines.first
  545. end
  546. end
  547. ##
  548. # Joins lines to string.
  549. #
  550. # @param [Array] lines line bodies without +\n+
  551. # @return [String] itself
  552. # @since 0.12.0
  553. #
  554. if not self.__hash_utils_instance_respond_to? :push_lines
  555. def push_lines(*lines)
  556. self.push("\n" << lines.join("\n"))
  557. end
  558. end
  559. if not self.__hash_utils_instance_respond_to? :push_line
  560. alias :push_line :push_lines
  561. end
  562. if not self.__hash_utils_instance_respond_to? :push
  563. alias :push :<<
  564. end
  565. ##
  566. # Removes appropriate number of characters from end of string.
  567. #
  568. # @param [Integer] count required number of characters
  569. # @return [String] removed characters
  570. # @since 0.12.0
  571. #
  572. if not self.__hash_utils_instance_respond_to? :pop
  573. def pop(count = 1)
  574. res = self[(self.length - count)..-1]
  575. self.replace(self[0..-(count + 1)])
  576. return res
  577. end
  578. end
  579. ##
  580. # Removes appropriate number of characters from begin of string.
  581. #
  582. # @param [Integer] count required number of characters
  583. # @return [String] removed characters
  584. # @since 0.12.0
  585. #
  586. if not self.__hash_utils_instance_respond_to? :shift
  587. def shift(count = 1)
  588. res = self[0...count]
  589. self.replace(self[count..-1])
  590. return res
  591. end
  592. end
  593. if Ruby::Version < [1, 9, 3]
  594. ##
  595. # Puts content to begin of string.
  596. #
  597. # @note Since Ruby 1.9.3 replaced by STL native version.
  598. #
  599. # @param [String] string string for prepend
  600. # @return [String] itself
  601. # @since 0.12.0
  602. #
  603. if not self.__hash_utils_instance_respond_to? :unshift
  604. def unshift(string)
  605. self.replace(string + self)
  606. end
  607. end
  608. if not self.__hash_utils_instance_respond_to? :prepend
  609. alias :prepend :unshift
  610. end
  611. else
  612. if not self.__hash_utils_instance_respond_to? :unshift
  613. alias :unshift :prepend
  614. end
  615. end
  616. if not self.__hash_utils_instance_respond_to? :append
  617. alias :append :<<
  618. end
  619. ##
  620. # Converts first character of the string to uppercase.
  621. #
  622. # @return [String] new string
  623. # @see http://www.php.net/ucfirst
  624. # @since 0.15.0
  625. #
  626. if not self.__hash_utils_instance_respond_to? :ucfirst
  627. def ucfirst
  628. self.dup.ucfirst!
  629. end
  630. end
  631. ##
  632. # Converts first character of the string to uppercase in place.
  633. #
  634. # @return [String] new string
  635. # @see http://www.php.net/ucfirst
  636. # @since 0.15.0
  637. #
  638. if not self.__hash_utils_instance_respond_to? :ucfirst!
  639. def ucfirst!
  640. self[0] = self.first.upcase
  641. return self
  642. end
  643. end
  644. ##
  645. # Converts first character of the string to lowercase.
  646. #
  647. # @return [String] new string
  648. # @see http://www.php.net/lcfirst
  649. # @since 0.15.0
  650. #
  651. if not self.__hash_utils_instance_respond_to? :lcfirst
  652. def lcfirst
  653. self.dup.lcfirst!
  654. end
  655. end
  656. ##
  657. # Converts first character of the string to lowercase in place.
  658. #
  659. # @return [String] new string
  660. # @see http://www.php.net/lcfirst
  661. # @since 0.15.0
  662. #
  663. if not self.__hash_utils_instance_respond_to? :lcfirst!
  664. def lcfirst!
  665. self[0] = self.first.downcase
  666. return self
  667. end
  668. end
  669. ##
  670. # Indicates, object is +String+.
  671. #
  672. # @return [Boolean] +true+ if yes, +false+ in otherwise
  673. # @since 0.17.0
  674. #
  675. if not self.__hash_utils_instance_respond_to? :string?
  676. def string?
  677. true
  678. end
  679. end
  680. ##
  681. # Swaps two strings. Return new content of self.
  682. #
  683. # @param [String] from source string
  684. # @return [String] new content of self
  685. # @since 0.18.0
  686. #
  687. if not self.__hash_utils_instance_respond_to? :swap_with
  688. def swap_with(from)
  689. intermediate = self.dup
  690. self.replace(from)
  691. from.replace(intermediate)
  692. return self
  693. end
  694. end
  695. if not self.__hash_utils_instance_respond_to? :swap_with!
  696. alias :swap_with! :swap_with
  697. end
  698. ##
  699. # Cuts string in place. Sets the content of #[] on place of
  700. # the string.
  701. #
  702. # @param [Range] range range with from and to limits
  703. # @return [String] itself
  704. # @since 0.18.0
  705. #
  706. if not self.__hash_utils_instance_respond_to? :cut!
  707. def cut!(range)
  708. self.replace(self[range])
  709. end
  710. end
  711. ##
  712. # Performs string interlacing. It means, it inserts given string
  713. # between characters of the string.
  714. #
  715. # @example
  716. # "abc".interlace("123") # will return "a123b123c"
  717. #
  718. # @param [String] string string which will interlaced to
  719. # @return [String] new interlaced string
  720. # @since 0.18.1
  721. #
  722. if not self.__hash_utils_instance_respond_to? :interlace
  723. def interlace(string)
  724. self.gsub(self.class::INTERLACING, '\1' << string << '\2' << string)
  725. end
  726. end
  727. ##
  728. # Performs string interlacing in place.
  729. #
  730. # @param [String] string string which will interlaced to
  731. # @return [String] itself
  732. # @see #interlace
  733. # @since 0.18.1
  734. #
  735. if not self.__hash_utils_instance_respond_to? :interlace!
  736. def interlace!(string)
  737. self.gsub!(self.class::INTERLACING, '\1' << string << '\2' << string)
  738. return self
  739. end
  740. end
  741. ##
  742. # Converts string to boolean. Note, it works differently than
  743. # {Object#to_b} because it converts to boolean by comparing in
  744. # contrast to +#to_b+ which converts by regular Ruby object boolean
  745. # evaluation.
  746. #
  747. # @param [Object] t true equivalent
  748. # @return [Boolean] result of conversion
  749. # @since 0.19.0
  750. #
  751. if not self.__hash_utils_instance_respond_to? :to_boolean
  752. def to_boolean(t = "true")
  753. self == t
  754. end
  755. end
  756. private
  757. ##
  758. # Prepares matcher for #strtr.
  759. #
  760. def __prepare_strtr(defs, mode = nil)
  761. defs = defs.to_h(mode)
  762. keys = defs.keys
  763. keys.map! { |i| i.to_s }
  764. matcher = Regexp::new("(" + keys.join("|") + ")")
  765. return [defs, matcher]
  766. end
  767. ##
  768. # Prepares #gsub_f family methods.
  769. #
  770. def __prepare_gsub_f(from, to = nil, callback = nil, &block)
  771. if not callback.nil?
  772. newcall = Proc::new do |s|
  773. callback.call(s.match(from))
  774. end
  775. end
  776. block.call(newcall)
  777. end
  778. end