How do you trim whitespace from the start and end of a string?
trim \" abc \"
=>
\"abc\"
Edit:
Ok, let me be a little cleare
Along the lines of what other people have suggested, you can avoid having to reverse your string by using:
import Data.Char (isSpace)
dropFromTailWhile _ [] = []
dropFromTailWhile p item
| p (last items) = dropFromTailWhile p $ init items
| otherwise = items
trim :: String -> String
trim = dropFromTailWhile isSpace . dropWhile isSpace
For sure, Data.Text is better for performance. But, as was mentioned, it's just fun to do it with lists. Here is a version that rstrip's the string in single pass (without reverse and ++) and supports infinite lists:
rstrip :: String -> String
rstrip str = let (zs, f) = go str in if f then [] else zs
where
go [] = ([], True)
go (y:ys) =
if isSpace y then
let (zs, f) = go ys in (y:zs, f)
else
(y:(rstrip ys), False)
p.s. as for infinite lists, that will work:
List.length $ List.take n $ rstrip $ cycle "abc "
and, for obvious reason, that will not (will run forever):
List.length $ List.take n $ rstrip $ 'a':(cycle " ")
This should be right about O(n), I believe:
import Data.Char (isSpace)
trim :: String -> String
-- Trimming the front is easy. Use a helper for the end.
trim = dropWhile isSpace . trim' []
where
trim' :: String -> String -> String
-- When finding whitespace, put it in the space bin. When finding
-- non-whitespace, include the binned whitespace and continue with an
-- empty bin. When at the end, just throw away the bin.
trim' _ [] = []
trim' bin (a:as) | isSpace a = trim' (bin ++ [a]) as
| otherwise = bin ++ a : trim' [] as
Nowadays the MissingH
package ships with a strip function:
import Data.String.Utils
myString = " foo bar "
-- strip :: String -> String
myTrimmedString = strip myString
-- myTrimmedString == "foo bar"
So if the conversion from String
to Text
and back does not make sense in your situation, you could use the function above.
I don't know anything about the runtime or efficiency but what about this:
-- entirely input is to be trimmed
trim :: String -> String
trim = Prelude.filter (not . isSpace')
-- just the left and the right side of the input is to be trimmed
lrtrim :: String -> String
lrtrim = \xs -> rtrim $ ltrim xs
where
ltrim = dropWhile (isSpace')
rtrim xs
| Prelude.null xs = []
| otherwise = if isSpace' $ last xs
then rtrim $ init xs
else xs
-- returns True if input equals ' '
isSpace' :: Char -> Bool
isSpace' = \c -> (c == ' ')
A solution without using any other module or library than the Prelude.
Some tests:
>lrtrim ""
>""
>lrtrim " "
>""
>lrtrim "haskell "
>"haskell"
>lrtrim " haskell "
>"haskell"
>lrtrim " h a s k e ll "
>"h a s k e ll"
It could be runtime O(n).
But I actually don't know it because I don't know the runtimes of the functions last and init. ;)
Another (std) solution
import System.Environment
import Data.Text
strip :: String -> IO String
strip = return . unpack . Data.Text.strip . pack
main = getLine >>= Main.strip >>= putStrLn