2017-09-14 3 views
0

1つまたは2つの数字とコロンとの間にスペース、数字、または行末を置きたいと思います。私のような文字列を持っている場合は、数字とコロンの間のスペースを排除する正規表現を作成するにはどうすればよいですか?

line = " 0 : 28 : 37.02" 

結果は次のようになります。

" 0: 28: 37.02" 

私は以下のように試してみました:

line.gsub!(/(\A|[ \u00A0|\r|\n|\v|\f])(\d?\d)[ \u00A0|\r|\n|\v|\f]:(\d|[ \u00A0|\r|\n|\v|\f]|\z)/, '\2:\3') 
# => " 0: 28 : 37.02" 

を最初":"に合わせているようだが、2番目の":"一致しません。なぜ私は理解できません。

+2

少なくともテストのためには、 'gsub! 'の代わりに' gsub'を使用してください。後者は 'line'を変更するので、実行するとコードを変更して再度実行し、' line'を再初期化しないと犬の朝食が得られ、何が起こっているのか完全に困惑します。とにかくバングバージョンを使いたいのはなぜわかりません。 –

答えて

1

は、負のルックバックで行うことができ、他の一つまたは二つの数字は、可変長であるためには、その部分について陽性ルックバックを使用することはできません。

line.gsub(/(?<!\d)(\d{1,2}) (?=:[ \d\$])/, '\1') 
# => " 0: 28: 37.02" 
+1

非常にいいですが、調整が必要です。 'line =" 0: "'なら、 '' 0: "'ではなく '' line'が返されます。先読みを '(?=:(?:[\ d] | $))'と書く必要があると思います。文字クラスのアンカーは無視されるようです。彼らがキャラクターではないからだと思う。その間に、先読みの前のスペースを '[[:space:]]'や '\ p {Space}'に変更して '' \\ 0AA:: ''が '' 0: "'になるようにすることもできます。 –

+0

キャプチャグループと逆参照の代わりに、 '\ K'(これまでのすべてのマッチを無視)を使うこともできます。' '0:28 \ u00A0:37.02" .gsub(/(?<! \ d)\ d {1,2} \ K [[:space: ](?:(?:[\ d] | \ z))/、 '')#=> "0:28:37.02 "。 –

+0

更新された答えのように、肯定的ではなくむしろ否定的な表現を使用することができました。 –

2

問題

私はそれが何をしているかを示すために(フリースペースモードでの)コメントをあなたの正規表現を定義します。 '|'は、文字クラス内の特殊文字(「や」)ではないことを

r = 
/
(      # begin capture group 1 
    \A      # match beginning of string (or does it?) 
    |      # or 
    [ \u00A0|\r|\n|\v|\f] # match one of the characters in the string " \u00A0|\r\n\v\f" 
)      # end capture group 1 
(\d?\d)     # match one or two digits in capture group 2 
[ \u00A0|\r|\n|\v|\f] # match one of the characters in the string " \u00A0|\r\n\v\f" 
:      # match ":" 
(      # begin capture group 3 
    \d      # match a digit 
    |      # or 
    [ \u00A0|\r|\n|\v|\f] # match one of the characters in the string " \u00A0|\r\n\v\f" 
    |      # or        
    \z      # match the end of the string 
)      # end capture group 3 
/x      # free-spacing regex definition mode 

注意。それは普通のキャラクターとして扱われます。 ('|'は次のように「または」文字クラスを一致させるために、その中に任意の1つの文字を強制するために使用されているので、何の目的を果たしていないだろう文字クラス内で処理した場合であっても。)

その後

line = " 0 : 28 : 37.02" 

を仮定それは文字ではなく、(それは例外を発生させない理由、私は知りませんが)文字だけが一致していないため、キャプチャグループ1ライン(\A)の初めに

line.gsub(r, '\2:\3') 
    #=> " 0: 28 : 37.02" 
$1 #=> " " 
$2 #=> "0" 
$3 #=> " " 

が一致しません。 "or"('|')の特殊文字は、正規表現エンジンが文字列" \u00A0|\r\n\v\f"の1文字に一致させようとします。したがって、文字列lineの先頭にある3つの空白のいずれかに一致します。

次のキャプチャグループ2は、"0"をキャプチャします。それを行うには、捕捉グループ1は、インデックス2のスペースを捕捉していなければならない。line。次にもう1つのスペースとコロンがマッチし、最後にキャプチャグループ3がコロンの後のスペースを取ります。

したがって、部分文字列' 0 : ''\2:\3' #=> '0: 'に置き換えられるため、gsub" 0: 28 : 37.02"を返します。 '0'より前の1つのスペースが削除されています(ただし、保持されているはずです)。

ソリューション

ここでは、1つまたは2つの数字(ない以上)で先行している1つのまたは複数のUnicodeの空白文字の最後を削除することができる方法だとの末尾にコロンが続いています文字列またはコロンの後ろに空白または数字が続きます。 (やれやれ!)

def trim(str) 
    str.gsub(/\d+[[:space:]]+:(?![^[:space:]\d])/) do |s| 
    s[/\d+/].size > 2 ? s : s[0,s.size-2] << ':' 
    end 
end 

正規表現がで(否定先読み)が続いていない、(これらすべての文字が一致している)コロン、一つ以上の空白文字に続いて、「マッチ一つ以上の数字を読み込み、ユニコードの空白または数字以外の文字 "を含む。一致がある場合は、最初に何桁の数字があるかを確認します。 2つ以上の一致が返された場合(変更なし)、一致する文字がコロンの前の空白文字から削除され、修正された一致が返されます。

trim " 0 : 28 : 37.02" 
    #=> " 0: 28: 37.02" xxx 
trim " 0\v: 28 :37.02" 
    #=> " 0: 28:37.02" 
trim " 0\u00A0: 28\n:37.02" 
    #=> " 0: 28:37.02" 
trim " 123 : 28 : 37.02" 
    #=> " 123 : 28: 37.02" 
trim " A12 : 28 :37.02" 
    #=> " A12: 28:37.02" 
trim " 0 : 28 :" 
    #=> " 0: 28:" 
trim " 0 : 28 :A" 
    #=> " 0: 28 :A" 

の例のように、文字列内の文字だけが数字、空白文字とコロンある、場合、後読みは必要ありません。

POSIX式[[:space:]]の代わりに、構造体\p{Space}を使用できます。両方とも、例に示されているものを含むUnicode空白文字のクラスに一致します。 3桁目を除く

+0

ありがとう、しかし私の質問の最後の部分に - 私は ":"の後に空白、数字または行の終わりが続く場合にのみマッチングを行いたいと考えていますか? – Dave

+0

ありがとう、Dave。私は本当により慎重に質問を読む必要があります。私はまた、2桁以上の数字がマッチしないという要件を欠いていました。私は編集をしました。 –

1
" 0 : 28 : 37.02".gsub!(/(\d)(\s)(:)/,'\1\3') 
=> " 0: 28: 37.02" 
+0

提案された解決策のうち、これが私が読んですぐに知ることができる唯一のものです。 – moveson

+0

":"の後にスペース、数字、または行の終わりが続く場合にのみ、この回答をどのように適用しますか? – Dave

関連する問題