将数组的内容写入perl中的txt文件

时间:2013-04-07 18:54:40

标签: arrays perl file-io

我想要实现的输出看起来像这样:

Name age gpa

对于每个数组中的每个元素,但是当我将数组的内容写入文件时,它会在某处写入并显示如下:

name1 name2 age1

这是我的代码,非常感谢所有帮助(即使是我的编码风格)。我只显示了我认为必要的代码部分,如果需要更多帮助请告诉我。

sub get_name
{
    printf("Name :");
    push(@get_name, <STDIN>); #taking name from user and putting it in @get_name array
    chomp(@get_name); #erasing newline from input
    #return @get_name;
}       

sub get_age
{
    print "Age :";
    push(@get_age, <STDIN>); #taking name from user and putting it in @get_name array
    chomp(@get_age); #erasing newline from input
    #return @get_age;
}       

sub get_gpa
{
    print "GPA: ";
    push(@get_gpa,<STDIN>);  #taking name from user and putting it in @get_name array
    chomp(@get_gpa); #erasing newline from input
    #return @get_gpa; #returning value may be unnecessary
}       

sub continue
{
    print "Get student data: ";
     $stu_data = <STDIN>;

    if ($stu_data =~ m/[n]/)
    {
            print "\nEnter file name: "; $user_file = <STDIN>;
            open (FILE, ">>$user_file") or die $!;
            printf FILE "%-0.25s %3.3s %4.4s\n", @get_name, @get_age, @get_gpa;           #writing to user given file.
            exit;

    }
    elsif ($stu_data =~ m/[y]/)
    {       #possibly makes do while loop unnecessary
            &get_name;
            &get_age;
            &get_gpa;
            &continue;
    }


}
close FILE;

2 个答案:

答案 0 :(得分:1)

由于你采取输入的方式,按下列表,每个push(@get_name, <STDIN>);将读取行,直到用户按Ctrl-D。这是一个糟糕的用户界面,但它也意味着可以将多行推送到@get_name和朋友。每一行都是数组的一个元素。为了示范...

$ perl -we '@lines = <STDIN>;  for my $line (@lines) { print "Line: $line"; }'
Foo
Bar
Line: Foo
Line: Bar

如果用户按两次输入结束预期输入结束,则会发生这种情况。

您想要的是my $name = <STDIN>; push @names, $name;。这将只读一行$name,然后自动停止。然后将该单个名称推送到@names

然后在输出端还有另一个问题。 printf获取参数列表。 Perl不知道使用一个元素传递三个列表和传入包含三个元素的列表之间的区别。他们都被夷为平地。因此printf FILE "%-0.25s %3.3s %4.4s\n", @get_name, @get_age, @get_gpa只将所有名称,年龄和gpas混合成一个大列表,而printf只打印前三个。所以如果有两个学生,你就是name1, name2, age1, age2, gpa1, gpa2。你需要一个循环。

for my $idx (0..$#get_name) {
    printf FILE "%-0.25s %3.3s %4.4s\n", $get_name[$idx], $get_age[$idx], $get_gpa[$idx];
}

它表示从0循环到@get_name中的最高索引。这是一个减去循环中项目的数量。由于历史原因,数组从零开始计数。然后你可以在每个数组中输入printf第0个,第一个,第二个......条目。

更深入地说,有三种方法可以改进您的代码。

首先是更好地使用子程序。他们可以返回结果,而不是设置全局变量的例程。

sub get_name
{
    printf("Name :");
    my $name = <STDIN>;
    chomp($name);
    return $name;
}

# @names is a better name because it describes what it contains, not how you got it
push @names, get_name;

其余的相同。一旦你完成了这一点,很明显你不需要三个例程,只需要一个例程。唯一改变的是提示。

sub get_input {
    my($prompt) = @_;  # this pulls in subroutine arguments

    print("$prompt: ");  # no need to use printf here
    my $input = <STDIN>;
    chomp($input);

    return $input;
}

push @names, get_input("Name");
push @ages,  get_input("Age");
push @gpas,  get_input("GPA");

现在不是维护三个相同的例程,而是三个错误的机会,你有一个。

对于子例程名称,

continue是一个糟糕的选择,因为它是Perl中的关键字。你最好调用这个main,因为它是你程序的主要部分,这是一个典型的约定。

而不是continue调用自身,称为递归的东西,这在这里非常有用但不是必需的,最好使用while循环。

sub main {
    while(1) {  # this is how you say "loop forever"
        print "Please enter data about a student.\n";
        push @names, get_input("Name");
        push @ages,  get_input("Age");
        push @gpas,  get_input("GPA");

        print "Would you like to enter more data?\n";
        my $more = <STDIN>;
        last if !$more =~ m/y/;  # last exits the current loop
    }

    print "\nEnter file name to output the data to: ";
    my $data_file = <STDIN>;
    open(my $fh, ">>$data_file") or die $!;

    for my $idx (0..$#names) {
        printf $fh "%-0.25s %3.3s %4.4s\n", $names[$idx], $ages[$idx], $gpas[$idx];
    }
}

您的printf有问题。在%3.3s中,.3部分对字符串没有任何意义。对于数字,它意味着将其打印到三个小数位。但是你使用了%s这意味着一个字符串。我不知道你要去做什么。有了GPA我会假设你打算像2.30一样打印。那是%.2f(f表示“浮点数”,这是程序员说“十进制数”的混乱方式)。年龄总是一个整数,所以%3d。是的,d代表...整数。

如果您想再次读取此数据并分隔字段,通常的做法是使用制表符分隔字段。然后你可以在重新阅读时将每一行分成标签。将它们全部放在一起......

    for my $idx (0..$#names) {
        printf $fh "%s\t%3d\t%.2f\n", $names[$idx], $ages[$idx], $gpas[$idx];
    }

探索的下一步是重组数据。拥有关于每个学生在多个数组之间分割的数据使得协调和传递到子例程变得很尴尬。相反,你会使用哈希。

my %student;
$student{name} = get_input("Name");
$student{age}  = get_input("Age");
$student{gpa}  = get_input("GPA");
push @students, \%student;

@students现在包含一个哈希引用列表。这有点先进了,我稍微略读一下,但现在你可以得到第一个学生的名字。

my $first_student = $students[0];
my $name = $first_student->{name};

当把它们全部打印出来时,循环就像这样。

for my $student (@students) {
    printf $fh "%s\t%3d\t%.2f\n", $student->{name}, $student->{age}, $student->{gpa};
}

全部,它看起来像这样。

use strict;
use warnings;

main();

sub main {
    my @students;

    # Get students
    while(1) {  # this is how you say "loop forever"
        print "Please enter data about a student.\n";
        my %student;
        $student{name} = get_input("Name");
        $student{age}  = get_input("Age");
        $student{gpa}  = get_input("GPA");
        push @students, \%student;

        print "Would you like to enter more data?\n";
        my $more = <STDIN>;
        last unless $more =~ m/y/;  # last exits the current loop
    }

    # Get the file to output to
    print "\nEnter file name to output the data to: ";
    my $data_file = <STDIN>;
    open(my $fh, ">>$data_file") or die $!;

    # Dump the students into the file
    for my $student (@students) {
        printf $fh "%s\t%3d\t%.2f\n", $student->{name}, $student->{age}, $student->{gpa};
    }
}

sub get_input {
    my($prompt) = @_;  # this pulls in subroutine arguments

    print("$prompt: ");  # no need to use printf here
    my $input = <STDIN>;
    chomp($input);

    return $input;
}

答案 1 :(得分:0)

代码中的这一行总共打印了3个元素:(一个带有3个%s的printf元素):

printf FILE "%-0.25s %3.3s %4.4s\n", @get_name, @get_age, @get_gpa;

这不是你想要做的。它基本上将连接所有数组并打印前3个元素。如果所有数组都有两个元素,您将获得name1 name2 age1。如果所有数组都有3个elemens name1 name2 name3。等

试试这样:

for my $i (0..$#get_name) {
    printf FILE "%-0.25s %3.3s %4.4s\n", $get_name[$i], $get_age[$i], $get_gpa[$i];
}

有几种方法可以在perl中编写这段代码。这是类似c的版本。注意:此循环假定所有3个数组都具有相同数量的元素。