在一个字段中用空格替换逗号 - 来自.CSV文件

时间:2016-04-12 18:51:38

标签: perl parsing csv replace

我遇到了一个程序问题,该程序通过一个包含几百万条记录的CSV文件进行解析:每行中有两个字段包含用户输入的评论,有时他们在评论中使用逗号。如果有逗号输入,则该字段将包含在双引号中。我需要用空格替换在这些字段中找到的任何逗号。这是文件中的一个这样的一行,可以给你一个想法 -

1925,47365,2,650187016,1,1,"MADE FOR DRAWDOWNS, NEVER P/U",16,IFC 8112NP,Standalone-6,,,44,10/22/2015,91607,,B24W02651,,"PA-3, PURE",4/28/2015,1,0,,1,MAN,,CUST,,CUSTOM MATCH,0,TRUE,TRUE,O,C48A0D001EF449E3AB97F0B98C811B1B,POS.MISTINT.V0000.UP.Q,PROD_SMISA_BK,414D512050524F445F504F5331393235906F28561D2F0020,10/22/2015 9:29,10/22/2015 9:30

注意 - 我没有Text :: CSV模块,也没有在我正在使用的服务器上使用。

以下是我解析此文件的代码的一部分。我做的第一件事是连接前三个字段并将该连接字段添加到每一行。然后我想清除@fields [7,19]中的逗号,然后在三个字段中格式化DATE,在两个字段中格式化DATETIME。我唯一能解决的问题就是清除那些逗号 -

my @data;

# Read the lines one by one.

while ( $line = <$FH> ) {

    # split the fields, concatenate the first three fields,
    # and add it to the beginning of each line in the file

    chomp($line);
    my @fields = split(/,/, $line);

    unshift @fields, join '_', @fields[0..2];

    # remove user input commas in fields[7,19]

    $_ =                 for fields[7,19]; 

    # format DATE and DATETIME fields for MySQL/sqlbatch60

    $_ = join '-', (split /\//)[2,0,1] for @fields[14,20,23];
    $_ = Time::Piece->strptime($_,'%m/%d/%Y %H:%M')->strftime('%Y-%m-%d %H:%M') for @fields[38,39];

    # write the parsed record back to the file

    push @data, \@fields;
}

3 个答案:

答案 0 :(得分:1)

如果只是 ONLY 第八个麻烦 AND 的字段你确切地知道应该有多少字段,你可以这样做

假设字段总数始终 N

  • 在逗号,
  • 上拆分该行
  • 分开并存储前六个字段
  • 分隔并存储最后n个字段,其中n为N-8
  • 使用逗号,重新加入剩余的内容。现在形成字段8

然后做你喜欢做的事情。例如,将其写入正确的 CSV文件

答案 1 :(得分:0)

Text::CSV_XS处理引用的逗号就好了:

 var automationElements = AutomationElement.FromHandle(proc.MainWindowHandle);

// Find `New Tab` element

var propCondNewTab = new PropertyCondition(AutomationElement.NameProperty, "New Tab");
var elemNewTab = automationElements.FindFirst(TreeScope.Descendants, propCondNewTab);

// Get parent of `New Tab` element
var treeWalker = TreeWalker.ControlViewWalker;
var elemTabStrip = treeWalker.GetParent(elemNewTab);

// Loop through all tabs
var tabItemCondition = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.TabItem);
foreach (AutomationElement tabItem in elemTabStrip.FindAll(TreeScope.Children, tabItemCondition)) {
    var nameProperty = tabItem.GetCurrentPropertyValue(AutomationElement.NameProperty);
    Debug.WriteLine("title: " + nameProperty.ToString());
}

答案 2 :(得分:0)

注意以下两个主要版本清理一个字段。该问题的最新变化表明事实上有两个这样的字段。可以修改代码,但我会先等待对此的反馈。 最后,第三个版本适用于任意数量的坏字段。所有代码都已使用提供的示例及其变体进行测试。

在澄清之后,这将处理需要手动处理文件的情况。很容易建议使用模块来解析.csv,但这里存在一个问题:依赖用户输入双引号。如果他们最终没有在那里我们有一个格式错误的文件。

我认为文件中的字段数是确定的,并且是提前知道的。

下面的两个独立解决方案使用数组或字符串处理。

(1)无论如何都要逐行处理文件,该行已被拆分。如果字段多于预期,则按空格连接额外的数组元素,然后使用正确的字段覆盖数组。这类似于vanHoesel在答案中概述的内容。

use strict;
use warnings;

my $num_fields = 39;  # what should be, using the example
my $ibad = 6;         # index of the malformed field-to-be
my @last = (-($num_fields-$ibad-1)..-1);  # index-range, rest of fields

my $file = "file.csv";
open my $fh, '<', $file;

while (my $line = <$fh>) {   # chomp it if needed
   my @fields = split ',', $line;
   if (@fields != $num_fields) {
       # join extra elements by space
       my $fixed = join ' ', @fields[$ibad..$ibad+@fields-$num_fields];
       # overwrite array by good fields
       @fields = (@fields[0..$ibad-1], $fixed, @fields[@last]);
   }   
   # Process @fields normally
   print "@fields";
}   
close $fh;

(2)预处理文件,仅检查格式错误的行并根据需要修复它们。使用字符串操作。 (或者,可以使用上述方法。)$num_fields$ibad是相同的。

while (my $line = <$fh>) {
    # Number of fields: commas + 1  (tr|,|| counts number of ",")
    my $have_fields = $line =~ tr|,|| + 1;
    if ($have_fields != $num_fields) {   
        # Get indices of commas delimiting the bad field
        my ($beg, $end) = map {
            my $p = '[^,]*,' x $_; 
            $line =~ /^$p/ and $+[0]-1; 
        } ($ibad, $ibad+$have_fields-$num_fields);

        # Replace extra commas and overwrite that part of the string
        my $bad_field = substr($line, $beg+1, $end-$beg-1);
        (my $fixed = $bad_field) =~ tr/,/ /;
        substr($line, $beg+1, $end-$beg-1) = $fixed;
    }   
    # Perhaps write the line out, for a corrected .csv file
    print $line;
}   

在最后一行中,$line的坏部分被分配给substr,这个函数允许的内容被覆盖。新的子字符串$fixed是使用逗号更改(或删除,如果需要)构造的,并用于覆盖$line的坏部分。见文档。

如果已知引号,则可以使用正则表达式。 适用于任意数量的错误字段。

while (my $line = <$fh>) {
    $line =~ s/."([^"]+)"/join ' ', split(',', $1)/eg;  # "
    # process the line. note that double quotes are removed
}

如果要保留引号,请将它们移到括号内,以便进行捕捉。

while (...) { 之后需要完成这一行,以清理数据。

/e修饰符使得替换方被评估为代码,而不是用作双引号字符串。在那里,行的匹配部分("之间)用逗号分隔,然后用空格连接,从而修复字段。请参阅perlretut中“搜索并替换”下的最后一项。

所有代码都已在错误字段中使用多行和多个逗号进行测试。