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