Comment couper les espaces blancs au début et à la fin d'une chaîne?
trim " abc "
=>
"abc"
Éditer:
Ok, permettez-moi d'être un peu plus clair. Je ne comprenais pas que les littéraux de chaînes étaient traités si différemment des chaînes.
Je voudrais faire ceci:
import qualified Data.Text as T
let s :: String = " abc "
in T.strip s
Est-ce possible à Haskell? J'utilise -XOverloadedStrings mais cela ne semble fonctionner que pour les littéraux.
Si vous avez de sérieux besoins de traitement de texte, utilisez le package text
de hackage:
> :set -XOverloadedStrings
> import Data.Text
> strip " abc "
"abc"
Si vous êtes trop têtu pour utiliser text
et que vous n'aimez pas l'inefficacité de la méthode inverse, alors peut-être (et je veux dire PEUT-ÊTRE) quelque chose comme ci-dessous sera plus efficace:
import Data.Char
trim xs = dropSpaceTail "" $ dropWhile isSpace xs
dropSpaceTail maybeStuff "" = ""
dropSpaceTail maybeStuff (x:xs)
| isSpace x = dropSpaceTail (x:maybeStuff) xs
| null maybeStuff = x : dropSpaceTail "" xs
| otherwise = reverse maybeStuff ++ x : dropSpaceTail "" xs
> trim " hello this \t should trim ok.. .I think .. \t "
"hello this \t should trim ok.. .I think .."
J'ai écrit ceci en supposant que la longueur des espaces serait minimale, donc votre O(n) of ++
et reverse
sont peu préoccupants. Mais encore une fois, je ressens le besoin de dire que si vous êtes réellement préoccupé par les performances, vous ne devriez pas du tout utiliser String
- passez à Text
.
EDIT pour faire valoir mon point de vue, un benchmark Criterion rapide me dit que (pour une chaîne de mots particulièrement longue avec des espaces et ~ 200 espaces pré et post), mon découpage prend 1,6 ms, le découpage utilisant l'inverse prend 3,5 ms et Data.Text.strip
prend 0,0016 ms ...
De: http://en.wikipedia.org/wiki/Trim_ (programmation) #Haskell
import Data.Char (isSpace)
trim :: String -> String
trim = f . f
where f = reverse . dropWhile isSpace
Après que cette question a été posée (vers 2012) Data.List
got dropWhileEnd
ce qui facilite grandement les choses:
trim = dropWhileEnd isSpace . dropWhile isSpace
Inefficace mais facile à comprendre et à coller si besoin:
strip = lstrip . rstrip
lstrip = dropWhile (`elem` " \t")
rstrip = reverse . lstrip . reverse
Pour sûr, Data.Text est meilleur pour les performances. Mais, comme cela a été mentionné, c'est juste amusant de le faire avec des listes. Voici une version qui rstrip est la chaîne en un seul passage (sans reverse et ++) et supporte des listes infinies:
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. quant aux listes infinies, cela fonctionnera:
List.length $ List.take n $ rstrip $ cycle "abc "
et, pour une raison évidente, cela ne fonctionnera pas (fonctionnera pour toujours):
List.length $ List.take n $ rstrip $ 'a':(cycle " ")
De nos jours, le package MissingH
est livré avec une fonction strip
:
import Data.String.Utils
myString = " foo bar "
-- strip :: String -> String
myTrimmedString = strip myString
-- myTrimmedString == "foo bar"
Donc, si la conversion de String
à Text
et retour n'a pas de sens dans votre situation, vous pouvez utiliser la fonction ci-dessus.
Vous pouvez combiner Data.Text
's strip
avec ses fonctions un/packing pour éviter d'avoir des chaînes surchargées:
import qualified Data.Text as T
strip = T.unpack . T.strip . T.pack
lstrip = T.unpack . T.stripStart . T.pack
rstrip = T.unpack . T.stripEnd . T.pack
Le tester:
> let s = " hello "
> strip s
"hello"
> lstrip s
"hello "
> rstrip s
" hello"
Je sais que c'est un vieux post, mais je n'ai vu aucune solution qui implémente un bon vieux fold
.
Supprimez d'abord le premier espace blanc à l'aide de dropWhile
. Ensuite, en utilisant foldl'
et une simple fermeture, vous pouvez analyser le reste de la chaîne en un seul passage, et sur la base de cette analyse, passer ce paramètre informatif à take
, sans avoir besoin de reverse
:
import Data.Char (isSpace)
import Data.List (foldl')
trim :: String -> String
trim s = let
s' = dropWhile isSpace s
trim' = foldl'
(\(c,w) x -> if isSpace x then (c,w+1)
else (c+w+1,0)) (0,0) s'
in
take (fst trim') s'
La variable c
garde la trace des espaces combinés blancs et non blancs qui doivent être absorbés, et la variable w
garde la trace des espaces blancs du côté droit à supprimer.
Exécutions de test:
print $ trim " a b c "
print $ trim " ab c "
print $ trim " abc "
print $ trim "abc"
print $ trim "a bc "
Production:
"a b c"
"ab c"
"abc"
"abc"
"a bc"
Cela devrait être vrai pour O (n), je crois:
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
À l'instar de ce que d'autres personnes ont suggéré, vous pouvez éviter d'avoir à inverser votre chaîne en utilisant:
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
Je ne sais rien du runtime ou de l'efficacité mais qu'en est-il:
-- 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 == ' ')
Une solution sans utiliser d'autre module ou bibliothèque que le Prelude.
Quelques tests:
>lrtrim ""
>""
>lrtrim " "
>""
>lrtrim "haskell "
>"haskell"
>lrtrim " haskell "
>"haskell"
>lrtrim " h a s k e ll "
>"h a s k e ll"
Il peut s'agir du runtime O (n).
Mais je ne le sais pas car je ne connais pas les durées d'exécution des fonctions last et init. ;)