合并多个csv文件并在连接时添加列

1 投票
5 回答
2606 浏览
提问于 2025-04-16 12:12

我有一组文件想要导入到MySQL数据库中。

每个CSV文件的格式如下:

Header1;Header2;Header3;Header4;Header5
Data1;Data2;Data3;Data4;Data5;
Data1;Data2;Data3;Data4;Data5;
Data1;Data2;Data3;Data4;Data5;
Data1;Data2;Data3;Data4;Data5;

数据中可能会有空格、句点或冒号,但绝对不会有分号,所以分号可以作为有效的分隔符。数据中也不会有换行符或其他换行字符。

示例数据

2010.08.30 18:34:59
0.7508
String of characters with spaces in them

每个文件都有一个独特的名字,所有名字都遵循以下格式:
    Token1_Token2_Token3.csv

我想把很多这样的CSV文件(大约几百个)合并成一个CSV文件。文件大小从10KB到400MB不等。最终,我想把这个文件发送到MySQL中。关于去掉每个文件的标题行不用担心;我可以在MySQL中轻松处理。

我希望最终的CSV文件看起来像这样:

Header1,Header2,Header3,Header4,Header5,FileName
Data1,Data2,Data3,Data4,Data5,Token1
Data1,Data2,Data3,Data4,Data5,Token1
Data1,Data2,Data3,Data4,Data5,Token1
Data1,Data2,Data3,Data4,Data5,Token1
Data1,Data2,Data3,Data4,Data5,Token1

我对其他的Token不太在意。如果解决方案只是把每个CSV文件的名字放到Token1字段里,我也能接受,因为我可以在MySQL中轻松解析这些。

请帮帮我!我已经花了超过10个小时在这个本该相对简单的问题上。

可用的技术有:

    awk
    windows批处理
    linux bash
    powershell
    perl
    python
    php
    mysql-import

这是一个服务器,所以我不能编译任何东西,但如果你给我一个Java的解决方案,我一定会尝试在服务器上运行。

5 个回答

1

你可以试试这个简单粗暴的Perl小技巧来转换数据:

#!/usr/bin/perl
use strict;
use warnings;

# Open input file
my $inputfile = shift or die("Usage: $0 <filename>\n\n");
open F, $inputfile or die("Could not open input file ($!)\n\n");

# Split filename into an array
my @tokens = split("_", $inputfile);

my $isFirstline = 1;

# Iterate each line in the file
foreach my $line (<F>) {
    my $addition;

    chomp($line);    # Remove newline

    # Add the complete filename to the line at first line
    if ($isFirstline) {
        $isFirstline = 0;
        $addition    = ",$inputfile";
    } else {         # Add first token for the rest of the lines
        $addition = ",$tokens[0]";
    }

    # Split the data into @elements array
    my @elements = split(";", $line);

    # Join it using comma and add filename/token & a new line
    print join(",", @elements) . $addition . "\n";
}

close(F);
3

使用 Text::CSV

程序

#!/usr/bin/env perl

use strict;
use warnings;

use File::Find;
use Text::CSV;

my $semi_colon_csv = Text::CSV->new( { 'sep_char' => ';', } );
my $comma_csv = Text::CSV->new( {
    'sep_char' => ',',
    'eol'      => "\n",
} );

open my $fh_output, '>', 'output.csv' or die $!;

sub convert {
    my $file_name = shift;

    open my $fh_input, '<', $file_name or die $!;

    # header
    my $row = $semi_colon_csv->getline($fh_input);
    $comma_csv->print( $fh_output, [ @$row, $file_name ] );

    while ( $row = $semi_colon_csv->getline($fh_input) ) {
        pop @$row unless $row->[-1];  # remove trailing semi-colon from input
        my ($token) = ( $file_name =~ /^([^_]+)/ );
        $comma_csv->print( $fh_output, [ @$row, $token ] );
    }
}

sub wanted {
    return unless -f;
    convert($_);
}

my $path = 'csv';  # assuming that all your CSVs are in ./csv/
find( \&wanted, $path );

输出 (output.csv)

Header1,Header2,Header3,Header4,Header5,Token1_Token2_Token3.csv
Data1,Data2,Data3,Data4,Data5,Token1
Data1,Data2,Data3,Data4,Data5,Token1
Data1,Data2,Data3,Data4,Data5,Token1
Data1,Data2,Data3,Data4,Data5,Token1
2

信不信由你,这可能就这么简单:

awk 'BEGIN{OFS = FS = ";"} {print $0, FILENAME}' *.csv > newfile.csv

如果你想把字段分隔符从分号改成逗号:

awk 'BEGIN{OFS = ","; FS = ";"} {$1 = $1; print $0, FILENAME}' *.csv > newfile.csv

只包含第一个标记:

awk 'BEGIN{OFS = ","; FS = ";"} {$1 = $1; split(FILENAME, a, "_"); print $0, a[1]}' *.csv > newfile.csv

撰写回答