2011-11-07 59 views
1

我有一個SQL SELECT與許多行,每行看起來是這樣的轉儲:正則表達式不匹配的數據和日期

07/11/2011 16:48:08,07/11/2011 16:48:08,'YD','MANUAL',0,1,'text','text','text','text',,,,'text',,,0,0, 

我想要做的兩件事情到每一行:

  1. 替換所有日期Oracle的sysdate函數。日期也可以不帶小時(如07/11/2011)。
  2. null

這裏替換所有空值是我的嘗試:

$_ =~ s/,(,|\n)/,null$1/g;     # Replace no data by "null" 
$_ =~ s/\d{2}\/\d{2}\/d{4}.*?,/sysdate,/g; # Replace dates by "sysdate" 

但是,這將改變字符串:雖然我希望它是

07/11/2011 16:48:08,07/11/2011 16:48:08,'YD','MANUAL',0,1,'text','text','text','text',null,,null,'text',null,,0,0,null 

sysdate,sysdate,'YD','MANUAL',0,1,'text','text','text','text',null,null,null,'text',null,null,0,0,null 

我不明白爲什麼日期不匹配,爲什麼一些,,不被替換爲null

任何見解歡迎,提前致謝。

+1

請提供正確的預期輸出。 – FailedDev

+1

對我來說你的日期替換工作,但是,你的空問題似乎是第二個逗號實際上是由第一個匹配消耗的,所以當它繼續尋找下一個匹配時,它從逗號開始,然後離開你與2在一個行。 – Lucas

+1

這些'文本'字段可以包含帶引號的逗號嗎? – TLP

回答

1

\d{2}\/\d{2}\/d{4}.*?,沒有工作,因爲最後的d未被轉義。
如果,可以在任意一側,或開始/字符串的結尾,你可以做2個步驟:

第1步
s/(?:^|(?<=,))(?=,|\n)/null/g
擴展:

/ 
    (?:^   # Begining of line, ie: nothing behind us 
    | (?<=,)  # Or, a comma behind us 
) 
    # we are HERE!, this is the place between characters 
    (?= ,   # A comma in front of us 
    | \n   # Or, a newline in front of us 
) 
/null/g 
# The above regex does not consume, it just inserts 'null', leaving the 
# same search position (after the insertion, but before the comma). 

# If you want to consume a comma, it would be done this way: 
s/(?:^|(?<=,))(,|\n)/null$1/xg 
# Now the search position is after the 'null,' 

第2步
s/(?:^|(?<=,))\d{2}\/\d{2}\/\d{4}.*?(?=,|\n)/sysdate/g

或者,你可以將它們組合成一個單一的正則表達式,使用eval修改:
$row =~ s/(?:^|(?<=,))(\d{2}\/\d{2}\/\d{4}.*?|)(?=,|\n)/ length $1 ? 'sysdate' : 'null'/eg;

分佈看,它看起來像這樣

s{ 
    (?:^| (?<=,)) # begin of line or comma behind us 
    (    # Capt group $1 
     \d{2}/\d{2}/\d{4}.*?  # date format and optional non-newline chars 
    |       # Or, nothing at all 
    )     # End Capt group 1 
    (?= , | \n)  # comma or newline in front of us 
}{ 
    length $1 ? 'sysdate' : 'null' 
}eg 

如果不換行空格填充的機會

$row =~ s/(?:^|(?<=,))(?:([^\S\n]*\d{2}\/\d{2}\/\d{4}.*?)|[^\S\n]*)(?=,|\n)/ defined $1 ? 'sysdate' : 'null'/eg;

+0

非常感謝!日期替換工作正常,我忘記了最後一次逃脫。如果你可以請解釋第一個正則表達式,那真是太棒了。再次感謝! – m0skit0

+0

我更喜歡爲易讀性設置單獨的正則表達式。再次感謝! – m0skit0

+0

@ m0skit0 - 修改帖子以解釋逗號正則表達式。只要正則表達式不會互相干擾,就可以通過設計完成,只需2步即可完成。 – sln

0

也許。*?太貪婪,嘗試:

$_ =~ s/\d{2}\/\d{2}\/d{4}[^,]+,/sysdate,/g; 
+0

問題是最後的d沒有逃脫。愚蠢的錯誤。感謝您的回答:) – m0skit0

1

你可以這樣做:

$ cat perlregex.pl 
use warnings; 
use strict; 

my $row = "07/11/2011 16:48:08,07/11/2011 16:48:08,'YD','MANUAL',0,1,'text','text','text','text',,,,'text',,,0,0,\n"; 

print("$row\n"); 
while ($row =~ /,([,\n])/) { $row =~ s/,([,\n])/,null$1/; } 
print("$row\n"); 
$row =~ s/\d{2}\/\d{2}\/\d{4}.*?,/sysdate,/g; 
print("$row\n"); 

導致此:

$ ./perlregex.pl 
07/11/2011 16:48:08,07/11/2011 16:48:08,'YD','MANUAL',0,1,'text','text','text','text',,,,'text',,,0,0, 

07/11/2011 16:48:08,07/11/2011 16:48:08,'YD','MANUAL',0,1,'text','text','text','text',null,null,null,'text',null,null,0,0,null 

sysdate,sysdate,'YD','MANUAL',0,1,'text','text','text','text',null,null,null,'text',null,null,0,0,null 

這當然可以優化,但它跨越獲取點。

+0

如果其中一個「文本」字段包含逗號,該怎麼辦?例如。 ''富,,, bar''。 – TLP

+0

@TLP,好點,如果是這樣的話,我會切換到使用一個包來處理解析然後把我自己放回去。在過去我已經使用Text :: CSV來完成這一點。在這裏應該就足夠了,你只需要在每一行中讀取,然後根據需要寫一個新的行來代替值。 – Lucas

+0

@TLP的好處,但據我所知,文本字段不包含逗號。 – m0skit0

1

要替換的東西:,它可以作爲被寫入。通常向前看符號是這個更好的選擇:

$subject =~ s/(?<=,)(?=,|$)/null/g; 

說明:

" 
(?<=  # Assert that the regex below can be matched, with the match ending at this position (positive lookbehind) 
    ,   # Match the character 「,」 literally 
) 
(?=  # Assert that the regex below can be matched, starting at this position (positive lookahead) 
       # Match either the regular expression below (attempting the next alternative only if this one fails) 
     ,   # Match the character 「,」 literally 
    |   # Or match regular expression number 2 below (the entire group fails if this one fails to match) 
     \$   # Assert position at the end of the string (or before the line break at the end of the string, if any) 
) 
" 

Secodnly要更換日期:

$subject =~ s!\d{2}/\d{2}/\d{4}.*?(?=,)!sysdate!g; 

這幾乎與原來的正則表達式相同。只要用前視來替換最後一個。 (如果您不想替換它,請不要匹配。)

# \d{2}/\d{2}/\d{4}.*?(?=,) 
# 
# Match a single digit 0..9 «\d{2}» 
# Exactly 2 times «{2}» 
# Match the character 「/」 literally «/» 
# Match a single digit 0..9 «\d{2}» 
# Exactly 2 times «{2}» 
# Match the character 「/」 literally «/» 
# Match a single digit 0..9 «\d{4}» 
# Exactly 4 times «{4}» 
# Match any single character that is not a line break character «.*?» 
# Between zero and unlimited times, as few times as possible, expanding as needed (lazy) «*?» 
# Assert that the regex below can be matched, starting at this position (positive lookahead) «(?=,)» 
# Match the character 「,」 literally «,» 
+0

感謝您的回答和解釋。爲什麼要修正第二個正則表達式呢?我只是忘了逃避最後一次...:P – m0skit0

+0

你*不應該*糾正它,但通常我傾向於避免匹配我不想修改的東西。這是一個很好的做法。 – FailedDev

+0

謝謝,但我的問題必須做更多關於「爲什麼我的原始正則表達式不符合我的需要」:) – m0skit0