2011-11-07 32 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