Perl - 在csv中搜索特定字符串并提取紧随其后的字符

时间:2012-07-12 18:59:06

标签: regex perl parsing csv

我有一个包含2列的csv文件:ID和自由文本列。 ID列包含一个16个字符的字母数字id,但它可能不是单元格中唯一存在的数据:它可能是一个空白单元格,或者只包含16个字符ID的单元格,或者包含一堆东西以及以下内容 - “user_id = xxxxxxxxxxxxxxxx”

我想要的是以某种方式从任何单元格中提取16个字符的id。所以我需要: (a)忽略空白单元格 (b)提取整个单元格的内容,如果它具有连续的16个字符的字符串,其间没有空格 (c)寻找模式“user_id =”,然后提取紧随其后的16个字符

我看到很多用于模式匹配或查找/替换字符串等的Perl脚本,但我不确定如何在同一列上依次进行不同类型的解析/模式搜索和提取。正如您可能已经意识到的那样,我对Perl来说还是个新手。

4 个答案:

答案 0 :(得分:1)

我知道您希望(1)跳过不包含任何内容的行,或者不符合您的规范。 (2)如果它们是单元格的唯一内容,则捕获16个非空格字符。 (3)按照文字模式“user_id =”捕获16个非空格字符。

如果也可以捕获空格字符,如果它们遵循“user_id=”字面值,您可以在适当的位置将\S更改为.

我的解决方案使用Text::CSV来处理处理CSV文件的详细信息。您可以这样做:

use strict;
use warnings;
use autodie;
use open ':encoding(utf8)';
use utf8;
use feature 'unicode_strings';
use Text::CSV;
binmode STDOUT, ':utf8';

my $csv = Text::CSV->new( {binary => 1} ) 
    or die "Cannot use CSV: " . Text::CSV->error_diag;

while( my $row = $csv->getline( \*DATA ) ) {
    my $column = $row->[0];
    if( $column =~ m/^(\S{16})$/ || $column =~ m/user_id=(\S{16})/ ) {
        print $1, "\n";
    }
}

__DATA__
abcdefghijklmnop
user_id=abcdefghijklmnop
abcd fghij lmnop
randomdatAuser_id=abcdefghijklmnopMorerandomdata
user_id=abcd fghij lmnop
randomdatAuser_id=abcd fghij lmnopMorerandomdata

在您自己的代码中,您不会使用DATA文件句柄,但我假设您已知道如何open文件。

CSV是一种看似简单的格式。不要将其高可读性与解析简单性混淆。处理CSV时,最好使用经过充分验证的模块来提取列。其他解决方案可能会失败引用嵌入式逗号,转义的逗号,不平衡的引号以及我们大脑在运行中为我们修复的其他异常情况,但这会使纯正复制解决方案变得脆弱。

答案 1 :(得分:0)

好吧,我可以使用基本文件和正则表达式命令来设置你可能做你需要的东西(对于不熟悉perl的人的基本格式):

use strict;
use warnings;

open FILE "<:utf8", "myfile.csv";
#"slurp" the file into an array, each element is a line
my @lines = <FILE>;
my @idArray;
foreach my $line (@lines){
    #make two captures, the first we can ignore and both are optional
    $line =~ /^(user_id=|)([A-Za-z0-9]{16}|),/;
    #for display purposes, this is just the second captured group
    my $id = $2;
    #if the group actually has something in it, add it to your final array
    if($id){ push @idArray, $id; }
}

答案 2 :(得分:0)

例如,在下一个例子中只有第2行和第3行有效,所以在cell1(column1)中是

  • 字符串恰好是16个字符长,或
  • 拥有“user = 16charshere”

任何其他无效。

use 5.014;
use warnings;

while(<DATA>) {
    chomp;
    my($col1, @remainder) = split /\t/;
    say $2 if $col1 =~ m/^(|user=)(.{16})$/;
}
__DATA__
ToShort col2    not_valid
a123456789012345    col2    valid
user=b123456789012345   col2    valid
TooLongStringHereSoNotValidOne  col2    not_valid

在此示例中,列是TAB分隔的。

答案 3 :(得分:-1)

请提供(a)可用于测试解决方案的一些示例数据;(b)请尝试提供您目前为此问题编写的代码。

但是,您可能希望遍历表的所有行,然后split遍历字段,在某个字段上执行所有操作,执行业务逻辑,然后将所有内容写回来。

问题(c)由$idField =~ /user_id=(.{16})/; my $id = $1;

解决

如果user_id始终显示在一行的开头,则可以解决这个问题:for (<FILE>) {/^user_id=(.{16})/; ...}