У меня есть много дат в столбце в файле CSV, который мне нужно преобразовать из dd/mm/yyyy в формат yyyy-mm-dd. Например, 17/01/2010 должен быть преобразован в 2010-01-17.
Как я могу сделать это в Perl или Python?
У меня есть много дат в столбце в файле CSV, который мне нужно преобразовать из dd/mm/yyyy в формат yyyy-mm-dd. Например, 17/01/2010 должен быть преобразован в 2010-01-17.
Как я могу сделать это в Perl или Python?
>>> from datetime import datetime
>>> datetime.strptime('02/11/2010', '%d/%m/%Y').strftime('%Y-%m-%d')
'2010-11-02'
или более хакерским способом (который не проверяет достоверность значений):
>>> '-'.join('02/11/2010'.split('/')[::-1])
'2010-11-02'
>>> '-'.join(reversed('02/11/2010'.split('/')))
'2010-11-02'
Если у вас есть хорошо сформированные данные, состоящие только из одной даты в формате DD-MM-YYYY, то это работает:
# FIRST METHOD
my $ndate = join("-" => reverse split(m[/], $date));
Это работает на $date
холдинге "07/04/1776", но терпит неудачу в "этом 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)...)
блоки там не поддерживаются. Heck, Python даже не поддерживает свойства Unicode. Из-за этого его просто не подходит для регулярного выражения Unicode.
Но, если вы считаете, что это плохо в Python по сравнению с Perl (и это точно так), просто попробуйте любой другой язык. Я не нашел того, что еще хуже для такого рода работ.
Как вы видите, у вас возникают реальные проблемы, когда вы запрашиваете решения regex с нескольких языков. Во-первых, решения трудно сравнивать из-за различных ароматизаторов регулярных выражений. Но также потому, что ни один другой язык не может сравниться с Perl для власти, выразительности и ремонтопригодности в его регулярных выражениях. Это может стать еще более очевидным, как только Unicode войдет в изображение.
Итак, если вы просто хотели Python, вы должны были попросить только об этом. В противном случае его ужасно несправедливый состязание, которое Python почти всегда проигрывает; его слишком беспорядочно, чтобы в Python такие вещи были правильны, не говоря уже об исправлении и чистой. Thats спрашивая больше, чем это может произвести.
Напротив, регулярные выражения Perls превосходят оба этих параметра.
Использовать время:: шт. (в ядре с 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 (вы можете установить его с помощью apt-get
под Ubuntu):
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";
}
Что будет работать на трубе, конвертирует и печатает по одной дате в строке.