[英]Perl or Python: Convert date from dd/mm/yyyy to yyyy-mm-dd
我在CSV文件的列中有很多日期,我需要將其從dd / mm / yyyy轉換為yyyy-mm-dd格式。 例如,17/01/2010應轉換為2010-01-17。
我怎么能用Perl或Python做到這一點?
如果你保證有完整的數據只包含DD-MM-YYYY格式的單例日期,那么這有效:
# FIRST METHOD
my $ndate = join("-" => reverse split(m[/], $date));
這適用於持有“07/04/1776”的$date
但在“這個17/01/2010和那個01/17/2010那里”失敗了。 相反,使用:
# SECOND METHOD
($ndate = $date) =~ s{
\b
( \d \d )
/ ( \d \d )
/ ( \d {4} )
\b
}{$3-$2-$1}gx;
如果您更喜歡更“語法”的正則表達式,以便更容易維護和更新,您可以改為使用:
# THIRD METHOD
($ndate = $date) =~ s{
(?&break)
(?<DAY> (?&day) )
(?&slash) (?<MONTH> (?&month) )
(?&slash) (?<YEAR> (?&year) )
(?&break)
(?(DEFINE)
(?<break> \b )
(?<slash> / )
(?<year> \d {4} )
(?<month> \d {2} )
(?<day> \d {2} )
)
}{
join "-" => @+{qw<YEAR MONTH DAY>}
}gxe;
最后,如果您有Unicode數據,您可能需要更加小心。
# FOURTH METHOD
($ndate = $date) =~ s{
(?&break_before)
(?<DAY> (?&day) )
(?&slash) (?<MONTH> (?&month) )
(?&slash) (?<YEAR> (?&year) )
(?&break_after)
(?(DEFINE)
(?<slash> / )
(?<start> \A )
(?<finish> \z )
# don't really want to use \D or [^0-9] here:
(?<break_before>
(?<= [\pC\pP\pS\p{Space}] )
| (?<= \A )
)
(?<break_after>
(?= [\pC\pP\pS\p{Space}]
| \z
)
)
(?<digit> \d )
(?<year> (?&digit) {4} )
(?<month> (?&digit) {2} )
(?<day> (?&digit) {2} )
)
}{
join "-" => @+{qw<YEAR MONTH DAY>}
}gxe;
您可以看到這四種方法在面對如下樣本輸入字符串時如何執行:
my $sample = q(17/01/2010);
my @strings = (
$sample, # trivial case
# multiple case
"this $sample and that $sample there",
# multiple case with non-ASCII BMP code points
# U+201C and U+201D are LEFT and RIGHT DOUBLE QUOTATION MARK
"from \x{201c}$sample\x{201d} through\xA0$sample",
# multiple case with non-ASCII code points
# from both the BMP and the SMP
# code point U+02013 is EN DASH, props \pP \p{Pd}
# code point U+10179 is GREEK YEAR SIGN, props \pS \p{So}
# code point U+110BD is KAITHI NUMBER SIGN, props \pC \p{Cf}
"\x{10179}$sample\x{2013}\x{110BD}$sample",
);
現在讓$date
成為通過該數組的foreach
迭代器,我們得到這個輸出:
Original is: 17/01/2010
First method: 2010-01-17
Second method: 2010-01-17
Third method: 2010-01-17
Fourth method: 2010-01-17
Original is: this 17/01/2010 and that 17/01/2010 there
First method: 2010 there-01-2010 and that 17-01-this 17
Second method: this 2010-01-17 and that 2010-01-17 there
Third method: this 2010-01-17 and that 2010-01-17 there
Fourth method: this 2010-01-17 and that 2010-01-17 there
Original is: from “17/01/2010” through 17/01/2010
First method: 2010-01-2010” through 17-01-from “17
Second method: from “2010-01-17” through 2010-01-17
Third method: from “2010-01-17” through 2010-01-17
Fourth method: from “2010-01-17” through 2010-01-17
Original is: 𐅹17/01/2010–17/01/2010
First method: 2010-01-2010–17-01-𐅹17
Second method: 𐅹2010-01-17–2010-01-17
Third method: 𐅹2010-01-17–2010-01-17
Fourth method: 𐅹2010-01-17–2010-01-17
現在,讓我們假設你其實想匹配非ASCII數字。 例如:
U+660 ARABIC-INDIC DIGIT ZERO
U+661 ARABIC-INDIC DIGIT ONE
U+662 ARABIC-INDIC DIGIT TWO
U+663 ARABIC-INDIC DIGIT THREE
U+664 ARABIC-INDIC DIGIT FOUR
U+665 ARABIC-INDIC DIGIT FIVE
U+666 ARABIC-INDIC DIGIT SIX
U+667 ARABIC-INDIC DIGIT SEVEN
U+668 ARABIC-INDIC DIGIT EIGHT
U+669 ARABIC-INDIC DIGIT NINE
甚至
U+1D7F6 MATHEMATICAL MONOSPACE DIGIT ZERO
U+1D7F7 MATHEMATICAL MONOSPACE DIGIT ONE
U+1D7F8 MATHEMATICAL MONOSPACE DIGIT TWO
U+1D7F9 MATHEMATICAL MONOSPACE DIGIT THREE
U+1D7FA MATHEMATICAL MONOSPACE DIGIT FOUR
U+1D7FB MATHEMATICAL MONOSPACE DIGIT FIVE
U+1D7FC MATHEMATICAL MONOSPACE DIGIT SIX
U+1D7FD MATHEMATICAL MONOSPACE DIGIT SEVEN
U+1D7FE MATHEMATICAL MONOSPACE DIGIT EIGHT
U+1D7FF MATHEMATICAL MONOSPACE DIGIT NINE
所以假設你有一個數學等寬數字的日期,如下所示:
$date = "\x{1D7F7}\x{1D7FD}/\x{1D7F7}\x{1D7F6}/\x{1D7F8}\x{1D7F6}\x{1D7F7}\x{1D7F6}";
Perl代碼可以正常工作:
Original is: 𝟷𝟽/𝟷𝟶/𝟸𝟶𝟷𝟶
First method: 𝟸𝟶𝟷𝟶-𝟷𝟶-𝟷𝟽
Second method: 𝟸𝟶𝟷𝟶-𝟷𝟶-𝟷𝟽
Third method: 𝟸𝟶𝟷𝟶-𝟷𝟶-𝟷𝟽
Fourth method: 𝟸𝟶𝟷𝟶-𝟷𝟶-𝟷𝟽
我想你會發現Python有一個相當大腦損壞的Unicode模型,它缺乏對抽象字符和字符串的支持,無論內容如何都會讓寫這樣的東西變得非常困難。
在Python中編寫清晰的正則表達式也是很困難的,你可以將子表達式的聲明與它們的執行分離,因為那里不支持(?(DEFINE)...)
塊。 哎呀,Python甚至不支持Unicode屬性。 由於這個原因,它不適合Unicode正則表達式工作。
但是,嘿,如果你認為與Perl相比它在Python中很糟糕( 當然也是如此 ), 那就試試其他任何語言吧。 我沒有找到一個對這類工作來說還不差的人。
如您所見,當您要求使用多種語言的正則表達式解決方案時,您會遇到實際問題。 首先,由於不同的正則表達風味,難以比較解決方案。 但也因為沒有其他語言可以與Perl在正則表達式中的功能,表現力和可維護性進行比較。 一旦任意Unicode進入圖片,這可能會變得更加明顯。
所以,如果你只是想要Python,你應該只要求它。 否則,這將是一場非常不公平的比賽,Python幾乎總會失敗; 它只是太亂來獲得這樣的事情正確的Python,更不用說正確 和干凈 。 這比它能產生的要多得多。
相比之下,Perl的正則表達在這兩方面都表現出色。
>>> from datetime import datetime
>>> datetime.strptime('02/11/2010', '%d/%m/%Y').strftime('%Y-%m-%d')
'2010-11-02'
或更多的hackish方式(不檢查值的有效性):
>>> '-'.join('02/11/2010'.split('/')[::-1])
'2010-11-02'
>>> '-'.join(reversed('02/11/2010'.split('/')))
'2010-11-02'
使用Time :: Piece(自5.9.5以來的核心),與接受的Python解決方案非常相似,因為它提供了strptime和strftime函數:
use Time::Piece;
my $dt_str = Time::Piece->strptime('13/10/1979', '%d/%m/%Y')->strftime('%Y-%m-%d');
要么
$ perl -MTime::Piece
print Time::Piece->strptime('13/10/1979', '%d/%m/%Y')->strftime('%Y-%m-%d');
1979-10-13
$
使用Perl: datetime
Python包剛剛破解。 您可以使用正則表達式來交換周圍的日期部分,例如
echo "17/01/2010" | perl -pe 's{(\d+)/(\d+)/(\d+)}{$3-$2-$1}g'
如果你確實需要解析這些日期(例如計算他們的星期幾或其他日歷類型的操作),請查看DateTimeX :: Easy (您可以在Ubuntu下使用apt-get
安裝它):
perl -MDateTimeX::Easy -e 'print DateTimeX::Easy->parse("17/01/2010")->ymd("-")'
Perl:
while (<>) {
s/(^|[^\d])(\d\d)\/(\d\d)\/(\d{4})($|[^\d])/$4-$3-$2/g;
print $_;
}
然后你只需要運行:
perl MyScript.pl < oldfile.txt > newfile.txt
Perl的:
my $date =~ s/(\d+)\/(\d+)\/(\d+)/$3-$2-$1/;
在Perl中你可以做到:
use strict;
while(<>) {
chomp;
my($d,$m,$y) = split/\//;
my $newDate = $y.'-'.$m.'-'.$d;
}
以光榮的perl-oneliner形式:
echo 17/01/2010 | perl -p -e "chomp; join('-', reverse split /\//);"
但嚴肅地說,我會這樣做:
#!/usr/bin/env perl
while (<>) {
chomp;
print join('-', reverse split /\//), "\n";
}
這將適用於管道,每行轉換和打印一個日期。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.