我如何知道文件是否在Perl中以制表符或空格分隔?

时间:2009-03-30 22:14:53

标签: perl csv tab-delimited

我正在从HTML页面上传文件到Perl程序。上传文件后,我想确定文件是空格还是制表符分隔,并且所有值都是整数。如果不是这种情况,那么我想输出一些消息。

我正在考虑阅读文件的每个字符并检查它是否为整数。如果失败,那么我将显示输出消息。有更好的方法吗?

我查了几个例子,可以逐行读取整个文件,但是如何读取该行中的每个字符?我应该分割space还是tab,因为文件可以是?

7 个答案:

答案 0 :(得分:8)

分割两个空格和标签很容易:

my @fields = split /[ \t]/, $line;

但如果它只是一个或另一个,并且你不知道哪个提前,那就有点棘手了。如果您知道输入中应该有多少列,您可以尝试计算每行的空格数和制表符数,并查看是否有正确数量的分隔符。例如。如果应该有5列,并且每行看到4个选项卡,那么用户使用制表符作为分隔符是一个不错的选择。如果两者都不匹配,则返回错误。

检查整数值很简单:

for my $val ( @fields ) {
    die "'$val' is not an integer!" if $val !~ /^-?\d+$/;
}

答案 1 :(得分:3)

听起来没有关系,它是由空格或标签分隔的。您必须在某个时刻读取文件的所有字符以验证它们并解析它们。为什么要做这两个步骤。从文件中获取整数,直到遇到不是空格或有效整数的东西,然后抱怨(并可能回滚)

答案 2 :(得分:1)

  

我正在将文件上传到perl   来自html页面的程序。后   该文件已经上传了我想要的   确定文件是否是   (空格或制表符分隔)和所有   值是整数。如果不是这样的话   那么我想输出一些   消息。

此条件意味着您的数据应仅包含数字,空格和制表符(基本上应该是数字和空格,或仅包含数字和制表符)。

为此,只需将数据加载到变量,并检查它是否匹配:

$data =~ /\A[0-9 \t]+\z/;

如果它匹配 - 这意味着你将有一组由空格或制表符分隔的整数(它与用于分隔整数的字符并不相关)。

如果您的下一步是提取这些整数(听起来合乎逻辑),您可以通过以下方式轻松完成:

@integers = split /[ \t]+/, $data;

@integers = $data =~ /(\d+)/g;

答案 3 :(得分:0)

你的问题不是很清楚。听起来您希望数据采用这种格式:

123 456 789
234 567 890

换句话说,每行包含一个或多个数字组,由空格分隔。假设您正如原始问题中所说的那样一次处理一行文件,我会使用这个正则表达式:

/^\d+(\s+\d+)*$/

如果可以有负数,请改用:

/^-?\d+(\s+-?\d+)*$/

你的正则表达式与空白行不匹配,而且这一行也不会。这应该是应该的;我希望在这种情况下禁止空行(包括只包含空格的行)。但是,文件末尾可能有一个或多个空行。这意味着,一旦找到与上述正则表达式不匹配的行,您应该验证每个剩余行的长度为零。

但我在这里做了很多假设。如果这不是你想要做的,你需要给我们更详细的要求。此外,所有这些都是对数据格式的粗略验证。如果您只是存储数据,那很好,但如果您还想提取信息,则可能应该在该过程中进行验证。

答案 4 :(得分:0)

要添加答案,我会写一个简单明了的答案。这个版本:

  1. 只使用最基本的Perl函数和结构,所以任何知道甚至一点点Perl的人都应该很快得到它。不要冒犯或任何东西,作为一个新手并不羞耻 - 我只是想写一些你能够理解的东西,无论你的技能水平如何。
  2. 接受制表符或空格作为分隔符,允许它们自由混合。注释掉的代码将详细说明强制执行整个文档的整个文档。
  3. 在包含错误值时打印出错误消息。应该显示非法值及其出现的行。
  4. 允许您按照自己喜欢的方式处理数据。我不打算将它存储在一个数组或任何东西中,只需在一个点放一个...,然后在那里添加一些代码来对你想要的给定行上的数据进行任何处理执行。
  5. 所以这里是:

    use strict;
    use warnings;
    
    open(my $data, "<", $filename);
    # define $filename before this, or get it from the user
    
    my $whitespace = "\t ";
    
    chomp(my @data = <$data>);
    
    # check first line for whitespace to enforce...
    #if($data[0] =~ /\t/ and $data[0] !~ / /) {
    #  $whitespace = "\t";
    #} elsif($data[0] =~ / / and $data[0] !~ /\t/) {
    #  $whitespace = " ";
    #} else {
    #  warn "Warning: mixed whitespace on line 1 - ignoring whitespace.\n";
    #}
    
    foreach my $n (0 .. $#data) {
      my @fields = split(/[$whitespace]+/, $data[$n]);
      foreach my $f (@fields) {
        if($f !~ /-?\d/) { # \D will call "-12" invalid
          if($f =~ /\s/) {
            warn "Warning: invalid whitespace use at line $n - ignoring.\n";
          } else {
            warn "Warning: invalid value '$f' at line $n - ignoring.\n";
          }
        } else {
          ... # do something with $f, or...
        }
      }
      ... # do something with @fields if you want to process the whole list
    }
    

    有更好,更快,更紧凑,甚至更可读(取决于你问谁)的方法,但这个使用最基本的结构,任何Perl程序员都应该能够阅读这个,无论如何技能水平(好吧,如果你刚刚开始使用Perl作为第一语言,你可能不知道任何一种语言,但是你不应该尝试做这样的事情。)

    编辑:修复我的正则表达式以匹配整数。它之前是懒惰的,并且允许“12-4”,这显然不是一个整数(虽然它评估为一个 - 但这更复杂(好吧,不是真的,但它不是OP想要的(或者是它?它)将是一个有趣的功能(INSERT LISP JOKE HERE))))。谢谢wisnij - 我很高兴我重新阅读你的帖子,因为你写了比我更好的正则表达式。

答案 5 :(得分:-1)

你可以使用正则表达式。这就是Perl的名气; - )。

简单示例:

perl -ne 'if ($_=~/^(\d+\s+)+$/){print "yep\n";}'

只接受仅包含数字和空格的行。这应该让你去。

答案 6 :(得分:-1)

我假设您的格式和所需结果有几个方面。

  • 连续分隔符崩溃。
  • 数字可能不会换行,即新行有效分隔符。
  • 一个文件中的制表符和空格都可以。任何分隔符都可以接受。
  • 文件足够小,一次处理整个文件不会成为问题。

此外,我的代码接受任何空格作为分隔符。

use strict;
use warnings;

# Slurp whole file into a scalar.
my $file_contents;
{   local $/;
    $/ = undef;
    $file_contents = <DATA>;
}

# Extract and validate numbers
my @ints = grep validate_integer($_), 
                split( /\s+/, $file_contents ); 
print "@ints\n";


sub validate_integer {
    my $value = shift;

    # is it an integer?
    # add additional validation here.
    if( $value =~ /^-?\d+$/ ) {
        return 1;
    }

    # die here if you want a fatal exception.
    warn "Illegal value '$value'\n";
    return;
}

__DATA__
1 -2 3 4
5 8.8
-6
    10a b c10 -99-
    8   9 98- 9-8
10 -11  12  13

这导致:

Illegal value '8.8'
Illegal value '10a'
Illegal value 'b'
Illegal value 'c10'
Illegal value '-99-'
Illegal value '98-'
Illegal value '9-8'
1 -2 3 4 5 -6 8 9 10 -11 12 13

更新:

  • 修正负数的处理。
  • 将验证map替换为grep
  • 切换到split,而不是从re。
  • 获取非空格

如果要逐行处理文件,可以将grep包装在读取文件的循环中。