Perl - ошибка "Использовать неинициализированное значение", когда значение должно быть установлено?
Я сталкиваюсь с ошибкой "Использование неинициализированного значения" при чтении файлов во вложенных циклах while. Я сократил свой код до минимума и удалил имена файлов / каталогов для конфиденциальности:
#/usr/bin/perl -w
use strict;
use warnings;
use diagnostics;
my $line_gene = undef;
my $gene_name = undef;
my $gene_chr = undef;
my $gene_pos = undef;
my $line_pval = undef;
my $chr = undef;
my $pos_start = undef;
my $pos_end = undef;
my $pos_mid = undef;
my $pval = undef;
open(IN_GENE,"somefile_gene") || die "Failed, gene\n";
open(IN_PVAL,"somefile_pval") || die "Failed, pval\n";
while ($line_gene = <IN_GENE>) {
chomp $line_gene;
($gene_name,$gene_chr,$gene_pos) = split(/\t/,$line_gene);
while ($line_pval = <IN_PVAL>) {
chomp $line_pval;
($chr,$pos_start,$pos_end,undef,undef,$pval) = split(/\t/,$line_pval);
$pos_mid = ($pos_start + $pos_end)/2;
if ($gene_chr == $chr) {
print $gene_chr."\t".$chr."\n";
}
}
seek IN_PVAL, 0, 0;
}
exit;
Когда я запускаю этот код, я получаю следующее сообщение об ошибке:
Use of uninitialized value $gene_chr in numeric eq (==) at Xtest.pl line 36,
<IN_PVAL> line 5772 (#1)
а затем следуя этим "Use of uninitialized value"
предупреждения - это правильные строки, выводимые на стандартный ввод:
6 6
1 1
20 20
... ...
Если я явно не делаю что-то не так, я не могу понять, почему он думает, что $gene_chr
неинициализирован. Также интересно то, что для каждой строки входного файла <IN_PVAL>
печатает выше "Use of uninitialized value"
предупреждение от строки 1 до строки 5772 (см. предупреждение выше), за исключением того, что этот файл содержит только 2886 строк, то есть ровно половину 5772.
Ни один входной файл (<IN_GENE>
а также <IN_PVAL>
) имеет пустые строки, либо в середине, либо в конце, и оба формата отформатированы, как и ожидалось, с правильным количеством полей, ни одно из которых не является пустым.
Любой совет будет принят во внимание. Спасибо!
3 ответа
Если $gene_chr
не определено, то это означает, что для одной или нескольких строк somefile_gene
,
($gene_name,$gene_chr,$gene_pos) = split(/\t/,$line_gene);
возвращается undef
как его второе значение (или возвращающее менее двух значений, что на самом деле одно и то же).
Я могу думать о двух способах, которыми это могло произойти:
1) Строка (и) не содержит вкладок, поэтому вся нерасщепленная строка помещается в $gene_name
, Это может произойти из-за строки, которая ошибочно использует пробелы вместо вкладок для разделения полей.
2) После первого значения строки (и) содержат две последовательные вкладки. Это может произойти из-за того, что кто-то пытается сделать поля более "привлекательными" для визуального представления, если $gene_name
значительно варьируется по длине.
Также интересно то, что для каждой строки входного файла он печатает вышеупомянутое предупреждение "Использование неинициализированного значения" из строки 1 в строку 5772 (см. Предупреждение выше), за исключением того, что в этом файле всего 2886 строк, то есть ровно половина из 5772.
Номер строки, который он показывает, является просто счетчиком того, сколько строк было прочитано из файла. С вами seek
вернуться к началу файла на каждом проходе, вместо того, чтобы закрывать и открывать его снова, счетчик никогда не будет сброшен.
Номера строк, идущие от 1 до (2 * количество строк в файле pval), указывают на то, что ваши ошибки в файле pval находятся в первых двух строках, которые будут считаться 1..2886 и 2887..5772, соответственно. Если ошибки были позже в файле, счетчик был бы выше.
Кроме того, в качестве общего совета, если строки в файле pval не являются слишком длинными, я бы серьезно задумался о том, возможно ли прочитать его содержимое один раз в хэш-ключ, включенный в $pval_chr
а затем заменить весь внутренний цикл
if (exists $pval_hash{$gene_chr}) { ... do stuff ... }
Если у вас есть несколько строк в файле гена, это значительно повысит производительность, так как не нужно будет перечитывать файл pval для каждой строки в файле гена.
Поскольку мне не о чем продолжать, это в основном образованные догадки. Однако, с некоторой обратной связью, я думаю, что мы можем получить где-нибудь. Я добавил это как ответ, так как чувствовал, что это слишком много информации для комментария.
Анализ
По-видимому, в какой-то момент из разделения не хватает полей, чтобы присвоить значение $gene_chr
, Вот почему он становится неинициализированным. Вот эта строка:
($gene_name,$gene_chr,$gene_pos) = split(/\t/,$line_gene);
Это произойдет, если в строке вообще нет вкладок, иначе вы получите пустую строку, и ваша ошибка будет ""
не быть числовым в ==
, То есть:
Argument "" isn't numeric in numeric eq (==)
Так как ваши сообщения об ошибках содержат строки, в два раза превышающие максимальное количество строк во внутреннем файле цикла, я бы предположил, что ваши IN_GENE
Файл имеет только две строки данных. Кроме того, я бы предположил, что у него есть завершающая пустая строка, которую вы не заметили, и это является причиной ошибок. Это не полностью складывается, но стоит попробовать, если это решит вашу проблему.
Решение
Попробуйте добавить проверку, чтобы увидеть, есть ли у вас пустые строки. Что-то вроде:
...
while ($line_gene = <IN_GENE>) {
chomp $line_gene;
unless ($line_gene =~ /\S/) { # unless the line contains non-whitespace
warn "Warning: Blank line in gene file";
next;
}
Это предупредит вас о пустых строках и пропустит их. Предупреждение (а также другие ошибки) отправляются в STDERR, что означает, что вы можете отделить их от стандартного вывода в STDOUT.
"chr" является зарезервированным ключевым словом. Вы не должны использовать это для имени переменной, независимо от того, что это работает.
Вы должны добавить строку print Dumper($gene_chr,$chr,$line_pval);
после вашего раскола и use Data::Dumper;
на вершине. Он многое расскажет о ваших данных. Я полагаю, проблема может быть в ваших файлах данных.
Может также добавить exit if $. > 10
куда-нибудь выйти после 10 строк, прочитанных из файла, и облегчить отладку.