3

我遇到了一个非常复杂的问题(从我作为新手的角度来看),我不知道如何解决它。我能想到工作流程,但不能想到脚本。

我的文件 A 如下所示:Teacher (tab) Student1(space)Student2(space)..

Fiona       Nicole Sherry 
James       Alan Nicole
Michelle    Crystal 
Racheal     Bobby Dan Nicole

当有两个同名(例如,John1、John2)时,他们有时会在他们的名字旁边有数字。如果他们有两个以上的顾问,学生也可能重叠。

文件 B 是一个将教师组放在一起的文件。它看起来很相似,但值是用逗号分隔的。

Fiona       Racheal,Jack
Michelle    Racheal
Racheal     Fiona,Michelle
Jack        Fiona

文件 B 的趋势是一个键有多个值,每个值也成为一个键,以便轻松找到谁与谁分组。

我想要的输出是哪些学生可能会根据他们的老师/小组接受类似的教育。所以我希望脚本执行以下操作:

  1. 将文件 A 存储到哈希中并关闭
  2. 打开文件B,查看每个老师是否有学生(有的可能没有,实际名单很大..)。所以如果我带第一个老师,Fiona,它会在存储的文件中查找一个哈希表,看看是否有一个 Fiona。如果有(在本例中为 Nicole 和 Sherry),将它们分别作为新哈希表的新键弹出。

    while (<Group>) {
        chomp;
        $data=$_;
        $data=~/^(\S+)\s+(.*)$/;
        $TeacherA=$1;
        $group=$2; 
    
  3. 然后,看看与Fiona(Racheal,Jack)分组的教师组。一次带 1 人 (Racheal)

    if (defined??) {
        while ($list=~/(\w+)(.*)/) {
            $TeacherB=$1;
            $group=$2;
    
  4. 查看 Racheal 学生的文件 A。
  5. 将它们填充为步骤 2 中生成的学生键的值(逗号分隔)。
  6. 打印学生-学生和教师-教师组。

    Nicole  Bobby,Dan,Nicole    Fiona   Racheal
    Sherry  Bobby,Dan,Nicole    Fiona   Racheal
    

    由于 Fiona 小组的下一任老师 Jack 没有学生,所以他不会出现在这个结果中。例如,如果他有大卫,结果将是:

    Nicole  Bobby,Dan,Nicole    Fiona   Racheal
    Sherry  Bobby,Dan,Nicole    Fiona   Racheal
    Nicole  David               Fiona   Jack
    Sherry  David               Fiona   Jack
    

我很抱歉问了这么一个复杂而具体的问题。我希望其他正在做类似事情的人可以从答案中受益。非常感谢您的帮助和回复。你是我唯一的帮助来源。

4

2 回答 2

1

这是查看数据的一种相当奇怪的方式,但我想我让它按照您尝试的方式工作。看看你为什么希望数据是这样的会很有趣。也许下次提供列标题。知道你为什么以某种方式做某事通常会让你更容易想到实现它的方法。

所以这就是我所做的。不要混淆,我将文件 A 和文件 B 中的值放入标量,并更改了读取它们的部分。

my $file_a = qq~Fiona\tNicole Sherry
James\tAlan Nicole
Michelle\tCrystal
Racheal\tBobby Dan Nicole
~;

my $file_b = qq~Fiona\tRacheal,Jack
Michelle\tRacheal
Racheal\tFiona,Michelle
Jack\tFiona
~;

之后,继续阅读“文件”。

# 1: Store file A in a hash
my (%file_a);
foreach my $a (split /\n/, $file_a) {
  my @temp = split /\t/, $a;
  $file_a{$temp[0]} = $temp[1];
}

# 2: Go through file B
foreach my $b (split /\n/, $file_b) {
  my @line_b = split /\t/, $b;
  # Look in stored file A if the teacher is there
  if (exists $file_a{$line_b[0]}) {
    my (%new_hash_table, @teachers);
    # Put all the students of this teacher into a new hash
    $new_hash_table{$_} = '' foreach split / /, $file_a{$line_b[0]};

    # 3: Take one of the group of teachers who are grouped with the 
    # current teacher at a time
    foreach my $teacher (split /,/, $line_b[1]) {
      if (exists $file_a{$teacher}) {
        # 4: This teacher from the group has students listen in file A
        push @teachers, $teacher; # Store the teacher's name for print later
        foreach (keys %new_hash_table) {
          # 5: Fill the students as csv for the student keys from step 2
          $new_hash_table{$_} = join(',', split(/ /, $file_a{$teacher}));
        }
      }
    }
    foreach my $student (keys %new_hash_table) {
      # 6: Print...        
      print join("\t", 
        # Student-student relation
        $student, $new_hash_table{$student}, 
        # Teacher-teacher relation
        $line_b[0], @teachers);
      print "\n";
    }
  }
}

对我来说,提供以下输出:

Sherry  Bobby,Dan,Nicole    Fiona   Racheal
Nicole  Bobby,Dan,Nicole    Fiona   Racheal
Crystal Bobby,Dan,Nicole    Michelle    Racheal
Bobby   Crystal Racheal Fiona   Michelle
Nicole  Crystal Racheal Fiona   Michelle
Dan Crystal Racheal Fiona   Michelle

这可能很奇怪,因为我没有所有的价值观。

无论如何,对此有几件事要说。

在您的示例代码中,您使用了一个正则表达式$data=~/^(\S+)\s+(.*)$/;来获取一个简单的两列列表的值。使用split 运算符来做到这一点要容易得多。

当您从具有<FILEHANDLE>语法的文件中读取时,您可以将希望行进入的标量放入while循环的条件中,如下所示:

while (my $data = <GROUP>) {
      chomp $data

此外,通常以全大写形式编写文件句柄名称。

我建议你看看'Learning Perl'。里面的哈希和数组的基本概念应该足以处理这样的任务。希望这可以帮助。

于 2012-04-23T09:26:30.567 回答
1

我无法想象当您只需查看文件 A 就可以很好地了解谁正在接受类似的教育时,为什么您会想要这些冗余数据……但是这里有一种在 perl 中执行此操作的方法。

$data = {};
# pull in students
open(IN, "students.txt");
while(my $line = <IN>) {
  chomp($line);
  my ($teacher, @students) = split(/\s+/,$line);
  $data->{$teacher}->{students} = \@students;
}
close IN;
# pull in teachers
open(IN, "teachers.txt");
while(my $line = <IN>) {
  chomp($line);
  my ($teacher, $supporters) = split(/\s+/,$line);
  my @supporters = split(/,/,$supporters);
  $data->{$teacher}->{supporters} = \@supporters;
}
close IN;
# make the output
foreach my $teacher (keys %{$data}){
  foreach my $teacher_student (@{$data->{$teacher}->{students}}) {
    foreach my $supporter (@{$data->{$teacher}->{supporters}}){
      my $num_supporter_students = @{$data->{$supporter}->{students}} + 0;
      if($num_supporter_students) {

        print "$teacher_student\t" . 
              join(",",@{$data->{$supporter}->{students}}) .
              "\t$teacher\t$supporter\n";
      }
    }
  }
}

在问题中列出的数据上运行时,它返回:

Crystal Bobby,Dan,Nicole    Michelle    Racheal
Nicole  Bobby,Dan,Nicole    Fiona   Racheal
Sherry  Bobby,Dan,Nicole    Fiona   Racheal
Bobby   Nicole,Sherry   Racheal Fiona
Bobby   Crystal Racheal Michelle
Dan Nicole,Sherry   Racheal Fiona
Dan Crystal Racheal Michelle
Nicole  Nicole,Sherry   Racheal Fiona
Nicole  Crystal Racheal Michelle
于 2012-04-23T09:29:49.210 回答