46 votes

Comment supprimer les espaces répétés dans une chaîne de caractères ?

J'ai une ficelle :

"foo (2 espaces) bar (3 espaces) baaar (6 espaces) fooo"

Comment supprimer les espaces répétitifs afin qu'il n'y ait pas plus d'un espace entre deux mots ?

2 votes

Vous savez, il est facile de répondre à ce genre de question en passant en revue toutes les méthodes de String. Je recommande vivement de se familiariser avec la documentation des méthodes String, Array et Enumerable.

0 votes

Si vous ne savez pas par où commencer, consultez le site suivant http://ruby-doc.org/ puis cliquez sur le bouton API de base puis cliquez sur la classe String dans la colonne centrale supérieure.

1 votes

Pour la défense de l'OP, la suppression des espaces peut être réalisée de plusieurs façons, qui ne sont pas toutes les plus intuitives, en particulier lorsque vous regardez les résultats de l'analyse comparative.

104voto

Nakilon Points 11635

Chaîne#squeeze comporte un paramètre facultatif permettant de spécifier les caractères à écraser.

irb> "asd  asd asd   asd".squeeze(" ")
=> "asd asd asd asd"

Attention : si vous l'appelez sans paramètre, TOUS les caractères répétés, et pas seulement les espaces, seront supprimés :

irb> 'aaa     bbbb     cccc 0000123'.squeeze
=> "a b c 0123"

2 votes

Ce n'est pas vrai... il peut "endommager" la chaîne... l'exemple serait "50b2a6cc6d5a2fb4e7000006" où vous obtiendriez "50b2a6c6d5a2fb4e706".

15 votes

@xpepermint, notez le " " paramètre.

1 votes

Ow... j'ai raté ça, désolé :)

51voto

kurumi Points 10096
>> str = "foo  bar   bar      baaar"
=> "foo  bar   bar      baaar"
>> str.split.join(" ")
=> "foo bar bar baaar"
>>

4 votes

+1 pour une façon amusante de procéder, mais -1 pour une suggestion inefficace par rapport à d'autres alternatives plus appropriées.

1 votes

0 votes

@zetetic. Merci. Cela prouve une fois de plus que le split/join n'est pas un moyen amusant ou inefficace, ( comme je l'ai toujours su ) que la substitution de regex.

29voto

the Tin Man Points 69148

Mise à jour du benchmark à partir de la réponse de @zetetic :

require 'benchmark'
include Benchmark

string = "foo  bar   bar      baaar"
n = 1_000_000
bm(12) do |x|
  x.report("gsub      ")   { n.times { string.gsub(/\s+/, " ") } }
  x.report("squeeze(' ')") { n.times { string.squeeze(' ') } }
  x.report("split/join")   { n.times { string.split.join(" ") } }
end

Ce qui donne les valeurs suivantes lorsque je l'exécute sur mon bureau après l'avoir fait deux fois :

ruby test.rb; ruby test.rb
                  user     system      total        real
gsub          6.060000   0.000000   6.060000 (  6.061435)
squeeze(' ')  4.200000   0.010000   4.210000 (  4.201619)
split/join    3.620000   0.000000   3.620000 (  3.614499)
                  user     system      total        real
gsub          6.020000   0.000000   6.020000 (  6.023391)
squeeze(' ')  4.150000   0.010000   4.160000 (  4.153204)
split/join    3.590000   0.000000   3.590000 (  3.587590)

Le problème est que squeeze supprime tout caractère répété, ce qui donne une chaîne de sortie différente et ne répond pas au besoin de l'OP. squeeze(' ') répond aux besoins, mais ralentit son fonctionnement.

string.squeeze
 => "fo bar bar bar"

J'ai réfléchi à la façon dont la split.join pourrait être plus rapide et il ne semblait pas que cela puisse être le cas pour les grandes chaînes, j'ai donc ajusté le point de référence pour voir quel serait l'effet des longues chaînes :

require 'benchmark'
include Benchmark

string = (["foo  bar   bar      baaar"] * 10_000).join
puts "String length: #{ string.length } characters"
n = 100
bm(12) do |x|
  x.report("gsub      ")   { n.times { string.gsub(/\s+/, " ") } }
  x.report("squeeze(' ')") { n.times { string.squeeze(' ') } }
  x.report("split/join")   { n.times { string.split.join(" ") } }
end

ruby test.rb ; ruby test.rb

String length: 250000 characters
                  user     system      total        real
gsub          2.570000   0.010000   2.580000 (  2.576149)
squeeze(' ')  0.140000   0.000000   0.140000 (  0.150298)
split/join    1.400000   0.010000   1.410000 (  1.396078)

String length: 250000 characters
                  user     system      total        real
gsub          2.570000   0.010000   2.580000 (  2.573802)
squeeze(' ')  0.140000   0.000000   0.140000 (  0.150384)
split/join    1.400000   0.010000   1.410000 (  1.397748)

Les longues files d'attente font donc une grande différence.


Si vous utilisez gsub, alors gsub/ \s {2,}/, ' ') est légèrement plus rapide.

Pas vraiment. Voici une version du test de référence permettant de vérifier cette affirmation :

require 'benchmark'
include Benchmark

string = "foo  bar   bar      baaar"
puts string.gsub(/\s+/, " ")
puts string.gsub(/\s{2,}/, ' ')
puts string.gsub(/\s\s+/, " ")

string = (["foo  bar   bar      baaar"] * 10_000).join
puts "String length: #{ string.length } characters"
n = 100
bm(18) do |x|
  x.report("gsub")               { n.times { string.gsub(/\s+/, " ") } }
  x.report('gsub/\s{2,}/, "")')  { n.times { string.gsub(/\s{2,}/, ' ') } }
  x.report("gsub2")              { n.times { string.gsub(/\s\s+/, " ") } }
end
# >> foo bar bar baaar
# >> foo bar bar baaar
# >> foo bar bar baaar
# >> String length: 250000 characters
# >>                          user     system      total        real
# >> gsub                 1.380000   0.010000   1.390000 (  1.381276)
# >> gsub/\s{2,}/, "")    1.590000   0.000000   1.590000 (  1.609292)
# >> gsub2                1.050000   0.010000   1.060000 (  1.051005)

Si vous voulez de la rapidité, utilisez gsub2 . squeeze(' ') peut toujours tourner en rond autour d'un gsub Cependant, la mise en œuvre de l'accord n'est pas encore achevée.

27voto

tokland Points 29813

Note importante : il s'agit d'une réponse pour Ruby on Rails, pas pour Ruby tout court. (les deux Activesupport y Facets font partie de Rails gemme)

Pour compléter les autres réponses, notez que [Activesupport][1] et [Facets][1] fournissent [String#squish][2] ([update] caveat : il supprime également les nouvelles lignes dans la chaîne) :

>> "foo  bar   bar      baaar".squish
=> "foo bar bar baaar"

fonction [1] : http://www.rubydoc.info/docs/rails/2.3.8/ActiveSupport/CoreExtensions/String/Filters#squish-instance_method [2] : http://www.rubydoc.info/github/rubyworks/facets/String%3Asquish

9voto

Reiner Gerecke Points 5332

Utiliser une expression régulière pour faire correspondre les espaces blancs répétitifs (\s+) et le remplacer par un espace.

"foo    bar  foobar".gsub(/\s+/, ' ')
=> "foo bar foobar"

Cela correspond à tous les espaces blancs. Si vous souhaitez uniquement remplacer les espaces, utilisez / +/ au lieu de /\s+/ .

"foo    bar  \nfoobar".gsub(/ +/, ' ')
=> "foo bar \nfoobar"

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X