Защо не мога да използвам функцията map, за да създам добър хеш от обикновен файл с данни в Perl?

Публикацията е актуализирана. Моля, преминете към частта за решение, ако вече сте прочели публикувания въпрос. Благодаря!

Ето минимизирания код за показване на моя проблем:

Файлът с входни данни за тест е запазен от вградения Notepad на Windows като UTF-8 кодиране. Той има следните три реда:

abacus  æbәkәs
abalone æbәlәuni
abandon әbændәn

Скрипт файлът на Perl също е запазен от вградения Notepad на Windows като UTF-8 кодиране. Той съдържа следния код:

#!perl -w

use Data::Dumper;
use strict;
use autodie;
open my $in,'<',"./hash_test.txt";
open my $out,'>',"./hash_result.txt";

my %hash = map {split/\t/,$_,2} <$in>;
print $out Dumper(\%hash),"\n";
print $out "$hash{abacus}";
print $out "$hash{abalone}";
print $out "$hash{abandon}";

В изхода хеш-таблицата изглежда е наред:

$VAR1 = {
          'abalone' => 'æbәlәuni
',
          'abandon' => 'әbændәn',
          'abacus' => 'æbәkәs
'
        };

Но всъщност не е, защото получавам само две стойности вместо три:

æbәlәuni
әbændәn

Perl дава следното предупредително съобщение:

Use of uninitialized value $hash{"abacus"} in string at C:\test2.pl line 11, <$i n> line 3.

къде е проблема Може ли някой любезно да обясни? Благодаря.

Решението

Милиони благодарности на всички вас :) Сега най-накрая виновникът е открит и проблемът става поправим :) Както @Sinan проницателно посочи, сега съм 100% сигурен, че виновникът за причиняването на проблема, който описах по-горе, са двамата байтове от BOM, които Notepad добави към моя файл с данни, когато беше записан като UTF-8 и които по някакъв начин Perl не третира правилно. Въпреки че мнозина предполагаха, че трябва да използвам "‹:utf8" и ">:utf8" за четене и запис на файлове, работата е там, че тези utf-8 конфигурации не решават проблема. Вместо това те могат да причинят някои други проблеми.

За да разреша наистина проблема, всичко, от което всъщност се нуждая, е да добавя един ред код, за да принудя Perl да игнорира BOM:

#!perl -w

use Data::Dumper;
use strict;
use autodie;

open my $in,'<',"./hash_test.txt";
open my $out,'>',"./hash_result.txt";

seek $in,3,0; # force Perl to ignore the BOM!
my %hash = map {split/\t/,$_,2} <$in>;
print $out Dumper(\%hash);
print $out $hash{abacus};
print $out $hash{abalone};
print $out $hash{abandon};

Сега резултатът е точно това, което очаквах:

$VAR1 = {
          'abalone' => 'æbәlәuni
',
          'abandon' => 'әbændәn',
          'abacus' => 'æbәkәs
'
        };
æbәkәs
æbәlәuni
әbændәn

Моля, обърнете внимание, че скриптът е записан като UTF-8 кодиране и кодът не трябва да включва никакви utf-8 етикети, тъй като входният и изходният файл са предварително запазени като UTF-8 кодиране.

Накрая отново благодаря на всички вас. И благодаря, @Sinan, за проницателните насоки. Без твоята помощ щях да остана в тъмнината Бог знае колко дълго.

Забележка За да изясня малко повече, ако използвам:

open my $in,'<:utf8',"./hash_test.txt";
open my $out,'>:utf8',"./hash_result.txt";

my %hash = map {split/\t/,$_,2} <$in>;
print $out Dumper(\%hash);
print $out $hash{abacus};
print $out $hash{abalone};
print $out $hash{abandon};

Резултатът е следният:

$VAR1 = {
          'abalone' => "\x{e6}b\x{4d9}l\x{4d9}uni
",
          'abandon' => "\x{4d9}b\x{e6}nd\x{4d9}n",
          "\x{feff}abacus" => "\x{e6}b\x{4d9}k\x{4d9}s
"
        };
æbәlәuni
әbændәn

И предупредителното съобщение:

Use of uninitialized value in print at C:\hash_test.pl line 13,  line 3.

person Community    schedule 19.11.2009    source източник
comment
Кодът ви работи добре за мен, въпреки че бих chomp в картата {}   -  person Matteo Riva    schedule 19.11.2009
comment
@Синан, странно е. Сигурен съм, че записите са разделени с раздели и затова използвам \t. Но когато използвам \s като предложението на apbianco, нещата започват да се оправят. Е, наистина е странно!   -  person Mike    schedule 19.11.2009
comment
@Kemper, значи си тествал кода на машината си и всичко е наред? Въведох ръчно раздели, за да разделя записите, но кодът не успява за запис на първия ред.   -  person Mike    schedule 19.11.2009
comment
@Sinan, о, не, проблемът продължава.   -  person Mike    schedule 19.11.2009
comment
@Mike Вмъкнах текста във файл и стартирах програмата точно както е публикувано. Всички хеш елементи са там. Няма неинициализирана стойност предупреждения.   -  person Sinan Ünür    schedule 19.11.2009
comment
@all, ако кодът работи добре на други системи, тогава предполагам, че виновникът може да е моята система.   -  person Mike    schedule 19.11.2009
comment
@Sinan, благодаря, не съм 100% сигурен, но сега вярвам, че това вероятно е проблем с несъвместимостта на операционната система. Може би нещо, свързано с обработката на кодирането на операционната система.   -  person Mike    schedule 19.11.2009
comment
@Mike: Няма нищо общо с несъвместимостта на операционната система. Обърнете внимание на допълнителното празно място в ' abacus' във вашия изход на Dumper (което не се виждаше, преди да редактирам публикацията ви). Вижте актуализирания ми отговор.   -  person Sinan Ünür    schedule 19.11.2009
comment
@Sinan, допълнителната празна? Ще погледна пак. Благодаря!   -  person Mike    schedule 19.11.2009
comment
@Mike: Уверете се, че файлът е записан като UTF-8. Уверете се, че го четете като UTF-8. Уверете се, че изходът е UTF-8. Запазването на файла като UTF-8 и четенето му като такъв ще реши проблема. Записването на изхода на Dumper в UTF-8 манипулатор ще ви помогне да диагностицирате какво се случва. Почти съм сигурен, че допълнителното пространство в ' abacus' е BOM.   -  person Sinan Ünür    schedule 19.11.2009
comment
@Sinan, благодаря за всички съвети. Чета записа в wikiepdia за BOM.   -  person Mike    schedule 19.11.2009
comment
@Mike Проверката на това какво има във вашия входен файл и какво има във вашия изход също може да бъде полезно.   -  person Sinan Ünür    schedule 19.11.2009


Отговори (5)


Намирам предупредителното съобщение за малко подозрително. Той ви казва, че файловият манипулатор $in е на ред 3, когато трябва да е на ред 4, след като сте прочели последния ред.

Когато опитах вашия код, запазих входния файл с помощта на GVim, който е конфигуриран в моята система да записва като UTF-8, не видях проблема. Сега, когато го пробвах с Notepad, гледайки изходния файл, виждам:

"\x{feff}abacus" => "\x{e6}b\x{4d9}k\x{4d9}s
"

където \x{feff} е BOM.

Във вашия Dumper изход има фалшив празен знак преди abacus (където не сте посочили :utf8 за изходния манипулатор).

Както споменах първоначално (изгубен от безбройните редакции на тази публикация, благодаря за напомнянето hobbs), посочете '<:utf8', когато отваряте входния файл.

person Sinan Ünür    schedule 19.11.2009
comment
@Синан, благодаря. Но проблемът продължава и предупредителното съобщение остава същото. Както забелязвам, в моята система, когато файлът с данни е кодиран като utf8 и Perl скриптът също е записан като utf-8, не трябва да използвам формат ‹:utf8. - person Mike; 19.11.2009
comment
@Sinan, за да реша този проблем с липсващия запис на първия ред, изглежда, че трябва да добавя празен ред с интервал и табулация и друг интервал и \n. - person Mike; 19.11.2009
comment
@Sinan, виновникът Е системата. Запазих скрипта и файла с данни и изходния файл като системно кодиране по подразбиране, GB2312 и въпреки че някои знаци няма да се показват правилно, всички хеш елементи са там. - person Mike; 19.11.2009
comment
@Синан, благодаря! Да, това е \x{fefe}, което причинява проблема. Те се вмъкват в началото на първия ред, за да покажат, че данните са utf кодирани, но поради някаква причина моята операционна система не ги третира по правилния начин. Почти съм сигурен, че съм направил всичко необходимо, за да гарантирам, че данните се четат като utf-8 кодиране. - person Mike; 19.11.2009
comment
Notepad добавя BOM, когато записвате като UTF-8. За да прочетете това правилно, трябва да отворите с '<:utf8'. Това е 100% както трябва и няма проблем. - person hobbs; 19.11.2009
comment
@hobbs, благодаря. Но това, че добавянето на „‹:utf8“ не решава проблема ми. Моля, прочетете актуализираната ми публикация. Благодаря. - person Mike; 20.11.2009
comment
@Sinan, благодаря отново. Реших проблема. Моля, прочетете моята актуализирана публикация. Благодаря :) - person Mike; 20.11.2009

Ако искате да четете/пишете UTF8 файлове, трябва да се уверите, че всъщност ги четете като UTF8.

#! /usr/bin/env perl
use Data::Dumper;
open my $in,  '<:utf8', "hash_test.txt";
open my $out, '>:utf8', "hash_result.txt";

my %hash = map { chomp; split ' ', $_, 2 } <$in>;
print $out Dumper(\%hash),"\n";
print $out "$hash{abacus}\n";
print $out "$hash{abalone}\n";
print $out "$hash{abandon}\n";

Ако искате да бъде по-стабилен, препоръчително е да използвате :encoding(utf8) вместо :utf8, за четене на файл.

open my $in, '<:encoding(utf8)', "hash_test.txt";

Прочетете PerlIO за повече информация.

person Brad Gilbert    schedule 19.11.2009
comment
@Брад, благодаря. Но вече опитах всички тези конфигурации на utf-8, изглежда не са от полза. - person Mike; 19.11.2009
comment
Сигурни ли сте, че оригиналният текст е в UTF8? - person Brad Gilbert; 19.11.2009
comment
@Brad, да, 100% съм сигурен, че оригиналният текст е в UTF8. Без UTF-8 кодиране, моята операционна система просто няма да покаже тези знаци правилно. Моля, прочетете актуализираната ми публикация. - person Mike; 20.11.2009

Мисля, че отговорът ви може да стои точно пред вас. Резултатът от Data::Dumper, който публикувахте, е:

$VAR1 = {
          'abalone' => 'æbәlәuni
',
          'abandon' => 'әbændәn',
          'abacus' => 'æbәkәs
'
        };

Забелязвате ли знака между ' и abacus? Опитахте да получите достъп до третата стойност чрез $hash{abacus}. Това е неправилно поради този знак преди abacus в хеша Dumper(). Можете да опитате да го включите в цикъл, който трябва да се погрижи за него:

foreach my $k (keys %hash) {
  print $out $hash{$k};
}
person Jack M.    schedule 19.11.2009

split/\s/ вместо split/\t/

person apbianco    schedule 19.11.2009
comment
split /\s/ е различно от split ' '. За съжаление, много хора използват първото, а не второто, когато имат предвид второто. - person Sinan Ünür; 19.11.2009
comment
@apbianco, проблемът продължава. - person Mike; 19.11.2009
comment
@all, наистина е разочароващо :( Може би защото работя с Windows XP (китайска версия) и има несъвместимост на някои кодирания? Но вече взех предпазни мерки и файлът с данни е UTF-8. - person Mike; 19.11.2009
comment
Изчакайте: 'abacus' =› 'æbәkәs ' -- изрязахте ли и поставихте или написахте резултата? Това интервал в края на `æbәkәs' ли е? - person apbianco; 19.11.2009
comment
@apbianco Всъщност решаващият, който би довел до предупреждението, е допълнителният интервал в ' abacus'. Не се виждаше, преди да преформатирам поста му, за да използвам <pre>. - person Sinan Ünür; 19.11.2009
comment
@Svante Благодаря ви за корекцията. Редактирането в полето за коментари е податливо на грешки и аз забравих да коригирам това. Освен това, като хумористична странична бележка Няколко недоволни няма да имат нищо от това, като твърдят, че в Обединеното кралство се смята за напълно правилно да се използва различен от в предложна конструкция. И така? straightdope.com/columns/ read/2295/ Един от моите учители по английски в гимназията беше завършил Оксфорд. Обвинявам го за това. - person Sinan Ünür; 20.11.2009

Работи за мен. Сигурни ли сте, че вашият пример съвпада с действителния ви код и данни?

person sorpigal    schedule 19.11.2009
comment
@Sorpigal, благодаря, че се опитахте да бъдете полезен. Предполагам, че този отговор беше отрицателен, защото беше по-скоро коментар, отколкото отговор. - person Mike; 21.11.2009