%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1996-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% -module(string). -export([len/1,equal/2,concat/2,chr/2,rchr/2,str/2,rstr/2, span/2,cspan/2,substr/2,substr/3,tokens/2,chars/2,chars/3]). -export([copies/2,words/1,words/2,strip/1,strip/2,strip/3, sub_word/2,sub_word/3,left/2,left/3,right/2,right/3, sub_string/2,sub_string/3,centre/2,centre/3, join/2]). -export([to_upper/1, to_lower/1]). -import(lists,[reverse/1,member/2]). %%--------------------------------------------------------------------------- %%% BIFs -export([to_float/1, to_integer/1]). -spec to_float(String) -> {Float, Rest} | {error, Reason} when String :: string(), Float :: float(), Rest :: string(), Reason :: no_float | not_a_list. to_float(_) -> erlang:nif_error(undef). -spec to_integer(String) -> {Int, Rest} | {error, Reason} when String :: string(), Int :: integer(), Rest :: string(), Reason :: no_integer | not_a_list. to_integer(_) -> erlang:nif_error(undef). %%% End of BIFs %% Robert's bit %% len(String) %% Return the length of a string. -spec len(String) -> Length when String :: string(), Length :: non_neg_integer(). len(S) -> length(S). %% equal(String1, String2) %% Test if 2 strings are equal. -spec equal(String1, String2) -> boolean() when String1 :: string(), String2 :: string(). equal(S, S) -> true; equal(_, _) -> false. %% concat(String1, String2) %% Concatenate 2 strings. -spec concat(String1, String2) -> String3 when String1 :: string(), String2 :: string(), String3 :: string(). concat(S1, S2) -> S1 ++ S2. %% chr(String, Char) %% rchr(String, Char) %% Return the first/last index of the character in a string. -spec chr(String, Character) -> Index when String :: string(), Character :: char(), Index :: non_neg_integer(). chr(S, C) when is_integer(C) -> chr(S, C, 1). chr([C|_Cs], C, I) -> I; chr([_|Cs], C, I) -> chr(Cs, C, I+1); chr([], _C, _I) -> 0. -spec rchr(String, Character) -> Index when String :: string(), Character :: char(), Index :: non_neg_integer(). rchr(S, C) when is_integer(C) -> rchr(S, C, 1, 0). rchr([C|Cs], C, I, _L) -> %Found one, now find next! rchr(Cs, C, I+1, I); rchr([_|Cs], C, I, L) -> rchr(Cs, C, I+1, L); rchr([], _C, _I, L) -> L. %% str(String, SubString) %% rstr(String, SubString) %% index(String, SubString) %% Return the first/last index of the sub-string in a string. %% index/2 is kept for backwards compatibility. -spec str(String, SubString) -> Index when String :: string(), SubString :: string(), Index :: non_neg_integer(). str(S, Sub) when is_list(Sub) -> str(S, Sub, 1). str([C|S], [C|Sub], I) -> case prefix(Sub, S) of true -> I; false -> str(S, [C|Sub], I+1) end; str([_|S], Sub, I) -> str(S, Sub, I+1); str([], _Sub, _I) -> 0. -spec rstr(String, SubString) -> Index when String :: string(), SubString :: string(), Index :: non_neg_integer(). rstr(S, Sub) when is_list(Sub) -> rstr(S, Sub, 1, 0). rstr([C|S], [C|Sub], I, L) -> case prefix(Sub, S) of true -> rstr(S, [C|Sub], I+1, I); false -> rstr(S, [C|Sub], I+1, L) end; rstr([_|S], Sub, I, L) -> rstr(S, Sub, I+1, L); rstr([], _Sub, _I, L) -> L. prefix([C|Pre], [C|String]) -> prefix(Pre, String); prefix([], String) when is_list(String) -> true; prefix(Pre, String) when is_list(Pre), is_list(String) -> false. %% span(String, Chars) -> Length. %% cspan(String, Chars) -> Length. -spec span(String, Chars) -> Length when String :: string(), Chars :: string(), Length :: non_neg_integer(). span(S, Cs) when is_list(Cs) -> span(S, Cs, 0). span([C|S], Cs, I) -> case member(C, Cs) of true -> span(S, Cs, I+1); false -> I end; span([], _Cs, I) -> I. -spec cspan(String, Chars) -> Length when String :: string(), Chars :: string(), Length :: non_neg_integer(). cspan(S, Cs) when is_list(Cs) -> cspan(S, Cs, 0). cspan([C|S], Cs, I) -> case member(C, Cs) of true -> I; false -> cspan(S, Cs, I+1) end; cspan([], _Cs, I) -> I. %% substr(String, Start) %% substr(String, Start, Length) %% Extract a sub-string from String. -spec substr(String, Start) -> SubString when String :: string(), SubString :: string(), Start :: pos_integer(). substr(String, 1) when is_list(String) -> String; substr(String, S) when is_integer(S), S > 1 -> substr2(String, S). -spec substr(String, Start, Length) -> SubString when String :: string(), SubString :: string(), Start :: pos_integer(), Length :: non_neg_integer(). substr(String, S, L) when is_integer(S), S >= 1, is_integer(L), L >= 0 -> substr1(substr2(String, S), L). substr1([C|String], L) when L > 0 -> [C|substr1(String, L-1)]; substr1(String, _L) when is_list(String) -> []. %Be nice! substr2(String, 1) when is_list(String) -> String; substr2([_|String], S) -> substr2(String, S-1). %% tokens(String, Seperators). %% Return a list of tokens seperated by characters in Seperators. -spec tokens(String, SeparatorList) -> Tokens when String :: string(), SeparatorList :: string(), Tokens :: [Token :: nonempty_string()]. tokens(S, Seps) -> case Seps of [] -> case S of [] -> []; [_|_] -> [S] end; [C] -> tokens_single_1(reverse(S), C, []); [_|_] -> tokens_multiple_1(reverse(S), Seps, []) end. tokens_single_1([Sep|S], Sep, Toks) -> tokens_single_1(S, Sep, Toks); tokens_single_1([C|S], Sep, Toks) -> tokens_single_2(S, Sep, Toks, [C]); tokens_single_1([], _, Toks) -> Toks. tokens_single_2([Sep|S], Sep, Toks, Tok) -> tokens_single_1(S, Sep, [Tok|Toks]); tokens_single_2([C|S], Sep, Toks, Tok) -> tokens_single_2(S, Sep, Toks, [C|Tok]); tokens_single_2([], _Sep, Toks, Tok) -> [Tok|Toks]. tokens_multiple_1([C|S], Seps, Toks) -> case member(C, Seps) of true -> tokens_multiple_1(S, Seps, Toks); false -> tokens_multiple_2(S, Seps, Toks, [C]) end; tokens_multiple_1([], _Seps, Toks) -> Toks. tokens_multiple_2([C|S], Seps, Toks, Tok) -> case member(C, Seps) of true -> tokens_multiple_1(S, Seps, [Tok|Toks]); false -> tokens_multiple_2(S, Seps, Toks, [C|Tok]) end; tokens_multiple_2([], _Seps, Toks, Tok) -> [Tok|Toks]. -spec chars(Character, Number) -> String when Character :: char(), Number :: non_neg_integer(), String :: string(). chars(C, N) -> chars(C, N, []). -spec chars(Character, Number, Tail) -> String when Character :: char(), Number :: non_neg_integer(), Tail :: string(), String :: string(). chars(C, N, Tail) when N > 0 -> chars(C, N-1, [C|Tail]); chars(C, 0, Tail) when is_integer(C) -> Tail. %% Torbjörn's bit. %%% COPIES %%% -spec copies(String, Number) -> Copies when String :: string(), Copies :: string(), Number :: non_neg_integer(). copies(CharList, Num) when is_list(CharList), is_integer(Num), Num >= 0 -> copies(CharList, Num, []). copies(_CharList, 0, R) -> R; copies(CharList, Num, R) -> copies(CharList, Num-1, CharList++R). %%% WORDS %%% -spec words(String) -> Count when String :: string(), Count :: pos_integer(). words(String) -> words(String, $\s). -spec words(String, Character) -> Count when String :: string(), Character :: char(), Count :: pos_integer(). words(String, Char) when is_integer(Char) -> w_count(strip(String, both, Char), Char, 0). w_count([], _, Num) -> Num+1; w_count([H|T], H, Num) -> w_count(strip(T, left, H), H, Num+1); w_count([_H|T], Char, Num) -> w_count(T, Char, Num). %%% SUB_WORDS %%% -spec sub_word(String, Number) -> Word when String :: string(), Word :: string(), Number :: integer(). sub_word(String, Index) -> sub_word(String, Index, $\s). -spec sub_word(String, Number, Character) -> Word when String :: string(), Word :: string(), Number :: integer(), Character :: char(). sub_word(String, Index, Char) when is_integer(Index), is_integer(Char) -> case words(String, Char) of Num when Num < Index -> []; _Num -> s_word(strip(String, left, Char), Index, Char, 1, []) end. s_word([], _, _, _,Res) -> reverse(Res); s_word([Char|_],Index,Char,Index,Res) -> reverse(Res); s_word([H|T],Index,Char,Index,Res) -> s_word(T,Index,Char,Index,[H|Res]); s_word([Char|T],Stop,Char,Index,Res) when Index < Stop -> s_word(strip(T,left,Char),Stop,Char,Index+1,Res); s_word([_|T],Stop,Char,Index,Res) when Index < Stop -> s_word(T,Stop,Char,Index,Res). %%% STRIP %%% -spec strip(string()) -> string(). strip(String) -> strip(String, both). -spec strip(String, Direction) -> Stripped when String :: string(), Stripped :: string(), Direction :: left | right | both. strip(String, left) -> strip_left(String, $\s); strip(String, right) -> strip_right(String, $\s); strip(String, both) -> strip_right(strip_left(String, $\s), $\s). -spec strip(String, Direction, Character) -> Stripped when String :: string(), Stripped :: string(), Direction :: left | right | both, Character :: char(). strip(String, right, Char) -> strip_right(String, Char); strip(String, left, Char) -> strip_left(String, Char); strip(String, both, Char) -> strip_right(strip_left(String, Char), Char). strip_left([Sc|S], Sc) -> strip_left(S, Sc); strip_left([_|_]=S, Sc) when is_integer(Sc) -> S; strip_left([], Sc) when is_integer(Sc) -> []. strip_right([Sc|S], Sc) -> case strip_right(S, Sc) of [] -> []; T -> [Sc|T] end; strip_right([C|S], Sc) -> [C|strip_right(S, Sc)]; strip_right([], Sc) when is_integer(Sc) -> []. %%% LEFT %%% -spec left(String, Number) -> Left when String :: string(), Left :: string(), Number :: non_neg_integer(). left(String, Len) when is_integer(Len) -> left(String, Len, $\s). -spec left(String, Number, Character) -> Left when String :: string(), Left :: string(), Number :: non_neg_integer(), Character :: char(). left(String, Len, Char) when is_integer(Char) -> Slen = length(String), if Slen > Len -> substr(String, 1, Len); Slen < Len -> l_pad(String, Len-Slen, Char); Slen =:= Len -> String end. l_pad(String, Num, Char) -> String ++ chars(Char, Num). %%% RIGHT %%% -spec right(String, Number) -> Right when String :: string(), Right :: string(), Number :: non_neg_integer(). right(String, Len) when is_integer(Len) -> right(String, Len, $\s). -spec right(String, Number, Character) -> Right when String :: string(), Right :: string(), Number :: non_neg_integer(), Character :: char(). right(String, Len, Char) when is_integer(Char) -> Slen = length(String), if Slen > Len -> substr(String, Slen-Len+1); Slen < Len -> r_pad(String, Len-Slen, Char); Slen =:= Len -> String end. r_pad(String, Num, Char) -> chars(Char, Num, String). %%% CENTRE %%% -spec centre(String, Number) -> Centered when String :: string(), Centered :: string(), Number :: non_neg_integer(). centre(String, Len) when is_integer(Len) -> centre(String, Len, $\s). -spec centre(String, Number, Character) -> Centered when String :: string(), Centered :: string(), Number :: non_neg_integer(), Character :: char(). centre(String, 0, Char) when is_list(String), is_integer(Char) -> []; % Strange cases to centre string centre(String, Len, Char) when is_integer(Char) -> Slen = length(String), if Slen > Len -> substr(String, (Slen-Len) div 2 + 1, Len); Slen < Len -> N = (Len-Slen) div 2, r_pad(l_pad(String, Len-(Slen+N), Char), N, Char); Slen =:= Len -> String end. %%% SUB_STRING %%% -spec sub_string(String, Start) -> SubString when String :: string(), SubString :: string(), Start :: pos_integer(). sub_string(String, Start) -> substr(String, Start). -spec sub_string(String, Start, Stop) -> SubString when String :: string(), SubString :: string(), Start :: pos_integer(), Stop :: pos_integer(). sub_string(String, Start, Stop) -> substr(String, Start, Stop - Start + 1). %% ISO/IEC 8859-1 (latin1) letters are converted, others are ignored %% to_lower_char(C) when is_integer(C), $A =< C, C =< $Z -> C + 32; to_lower_char(C) when is_integer(C), 16#C0 =< C, C =< 16#D6 -> C + 32; to_lower_char(C) when is_integer(C), 16#D8 =< C, C =< 16#DE -> C + 32; to_lower_char(C) -> C. to_upper_char(C) when is_integer(C), $a =< C, C =< $z -> C - 32; to_upper_char(C) when is_integer(C), 16#E0 =< C, C =< 16#F6 -> C - 32; to_upper_char(C) when is_integer(C), 16#F8 =< C, C =< 16#FE -> C - 32; to_upper_char(C) -> C. -spec to_lower(String) -> Result when String :: io_lib:latin1_string(), Result :: io_lib:latin1_string() ; (Char) -> CharResult when Char :: char(), CharResult :: char(). to_lower(S) when is_list(S) -> [to_lower_char(C) || C <- S]; to_lower(C) when is_integer(C) -> to_lower_char(C). -spec to_upper(String) -> Result when String :: io_lib:latin1_string(), Result :: io_lib:latin1_string() ; (Char) -> CharResult when Char :: char(), CharResult :: char(). to_upper(S) when is_list(S) -> [to_upper_char(C) || C <- S]; to_upper(C) when is_integer(C) -> to_upper_char(C). -spec join(StringList, Separator) -> String when StringList :: [string()], Separator :: string(), String :: string(). join([], Sep) when is_list(Sep) -> []; join([H|T], Sep) -> H ++ lists:append([Sep ++ X || X <- T]).