Perl'de bir dosyanın tamamını bir dizeye nasıl okuyabilirim?


118

Bir .html dosyasını büyük, uzun bir dize olarak açmaya çalışıyorum. Elimde olan bu:

open(FILE, 'index.html') or die "Can't read file 'filename' [$!]\n";  
$document = <FILE>; 
close (FILE);  
print $document;

sonuç:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN

Ancak sonucun şöyle görünmesini istiyorum:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">

Bu şekilde tüm belgeyi daha kolay arayabilirim.


8
Gerçekten de "Yüklenemez" in tanımının ne olduğunu kontrol etmeliyim, bu yaygın bir sorundur ve genellikle yapılması gerekmeyen bir argümandır. stackoverflow.com/questions/755168/perl-myths/…
Kent Fredric

1
Aslında bu betiğin üzerinde çalıştığı sunucunun tamamı dışında hiçbir şeyi değiştiremiyorum.
allahım

Yani sunucunun herhangi bir yerine dosya eklemenize izin verilmiyor mu?
Brad Gilbert

FatPack modülleri betiğinize dahil mi? Ayrıca, HTML'yi normal ifadelerle ayrıştırmayı düşünüyor olabilirsiniz, değil.
MkV

Yanıtlar:


82

Ekle:

 local $/;

dosya tanıtıcısından okumadan önce. Bkz Bütün seferde tüm dosyasında okuyabilir nasıl?veya

$ perldoc -q "dosyanın tamamı"

Ve içindeki dosya tanıtıcılarıyla ilgili değişkenlere bakın .perldoc perlvarperldoc -f local

Bu arada, betiğinizi sunucuya koyabilirseniz, istediğiniz tüm modüllere sahip olabilirsiniz. Bkz Kendi modül / kütüphane dizini tutmak nasıl? .

Ek olarak, Path :: Class :: File höpürdetmenize ve kusmanıza izin verir .

Yol :: Minik gibi daha kolaylık yöntemlerini verir slurp, slurp_raw,slurp_utf8 yanı sıra bunların spewmuadilleri.


33
Muhtemelen $ / yerelleştirmenin ne gibi etkiler yapacağını ve amacının ne olduğunu açıklamalısınız.
Danny

12
Yerelleştirme hakkında hiçbir şey açıklamayacaksanız $/, muhtemelen daha fazla bilgi için bağlantılar eklemelisiniz.
Brad Gilbert

7
Ne yaptığına dair adım adım iyi bir açıklama: {local $ /; <$ fh>} burada sağlanır: perlmonks.org/?node_id=287647
dawez

Eğer kullanmalıdır Belki de neden sadece söylemek localdeğil my.
Geremia

@Geremia Kapsam belirleme tartışması bu cevabın kapsamı dışındadır.
Sinan Ünür

99

Bunu şöyle yapardım:

my $file = "index.html";
my $document = do {
    local $/ = undef;
    open my $fh, "<", $file
        or die "could not open $file: $!";
    <$fh>;
};

Open'ın üç bağımsız değişkenli versiyonunun kullanımına dikkat edin. Eski iki (veya bir) argüman versiyonundan çok daha güvenlidir. Ayrıca sözcüksel dosya tanıtıcısı kullanımına dikkat edin. Sözcüksel dosya tutamaçları, birçok nedenden dolayı eski açık sözlü varyantlardan daha iyidir. Burada bunlardan birinden yararlanıyoruz: kapsam dışına çıktıklarında kapanıyorlar.


9
Bu, muhtemelen hem 3 bağımsız değişkeni açık hem de INPUT_RECORD_SEPARATOR ($ /) değişkenini gereken en küçük bağlama göre yerelleştirilmiş olarak tuttuğu için bunu yapmanın en iyi cpan olmayan yoludur.
Danny

77

İle Dosya :: Slurp :

use File::Slurp;
my $text = read_file('index.html');

Evet, CPAN'ı bile kullanabilirsiniz .


OP, sunucudaki hiçbir şeyi değiştiremeyeceğini söyledi. Buradaki "Evet, CPAN'ı bile kullanabilirsiniz" bağlantısı, çoğu durumda bu sınırlamayı nasıl aşacağınızı gösterir.
Trenton

Can't locate File/Slurp.pm in @INC (@INC contains: /usr/lib/perl5/5.8/msys:(
Dmitry

2
@Dmitry - O halde modülü kurun. Bu yanıttan bağlantı verdiğim metacpan sayfasında bir yükleme talimatları bağlantısı var.
Quentin

53

Tüm gönderiler biraz deyimsel değildir. Deyim:

open my $fh, '<', $filename or die "error opening $filename: $!";
my $data = do { local $/; <$fh> };

Çoğunlukla, $ / to ayarlamaya gerek yoktur undef.


3
local $foo = undefsadece Perl Best Practice (PBP) tarafından önerilen yöntemdir. Kod parçacıkları gönderiyorsak, bunu netleştirmek için elimizden gelenin en iyisini yapmanın İyi Bir Şey olacağını düşünürdüm.
Danny

2
İnsanlara deyimsel olmayan kodların nasıl yazılacağını göstermek iyi bir şey mi? Üzerinde çalıştığım kodda "local $ / = undef" görürsem, ilk eylemim yazarı irc'de alenen aşağılamak olur. (Ve genellikle "stil" sorunları konusunda seçici değilim.)
jrockway

1
Tamam, biraz ısıracağım: "local $ / = undef" hakkında tam olarak alay konusu olan nedir? Tek cevabınız "Bu deyimsel değil" ise, o zaman (a) o kadar emin değilim ve (b) ne olmuş yani? O kadar emin değilim, çünkü bunu yapmanın bir yolu olarak son derece yaygın. Ve ne oldu çünkü tamamen açık ve makul derecede kısa. Düşündüğünüz stil sorunları hakkında daha seçici olabilirsiniz.
Telemachus

1
İşin püf noktası, "local $ /" iyi bilinen bir deyimin parçası olmasıdır. Rastgele bir kod yazıyorsanız ve "local $ Foo :: Bar = undef;" yazıyorsanız, sorun değil. Ama bu çok özel durumda, "daha az açık" olsa bile herkesle aynı dili konuşabilirsiniz (ki buna katılmıyorum; "yerel" davranışı bu açıdan iyi tanımlanmıştır).
jrockway

11
Üzgünüm, katılmıyorum. Bir sihirli değişkenin gerçek davranışını değiştirmek istediğinizde açık olmak çok daha yaygındır; bir niyet beyanıdır. Belgeler bile 'local $ / = undef' kullanıyor (bkz. Perldoc.perl.org/perlsub.html#Tporary-Values-via-local () )
Leonardo Herrera

19

Gönderen perlfaq5: Nasıl bir kerede tüm bütün bir dosyada okuyabilirim? :


Bunu tek adımda yapmak için File :: Slurp modülünü kullanabilirsiniz.

use File::Slurp;

$all_of_it = read_file($filename); # entire file in scalar
@all_lines = read_file($filename); # one line per element

Bir dosyadaki tüm satırları işlemek için geleneksel Perl yaklaşımı, bir seferde bir satır yapmaktır:

open (INPUT, $file)     || die "can't open $file: $!";
while (<INPUT>) {
    chomp;
    # do something with $_
    }
close(INPUT)            || die "can't close $file: $!";

Bu, tüm dosyayı bir satır dizisi olarak belleğe okumaktan ve ardından onu her seferinde tek bir öğe olarak işlemekten çok daha etkilidir, bu genellikle - hemen hemen her zaman değilse de - yanlış bir yaklaşımdır. Birinin bunu yaptığını gördüğünüzde:

@lines = <INPUT>;

Her şeyin aynı anda yüklenmesine neden ihtiyaç duyduğunuzu uzun uzun düşünmelisiniz. Bu sadece ölçeklenebilir bir çözüm değil. Ayrıca, standart Tie :: File modülünü veya DB_File modülünün $ DB_RECNO bağlarını kullanmak daha eğlenceli olabilir; bu, bir diziyi bir dosyaya bağlamanıza izin verir, böylece bir öğeye erişildiğinde, dizinin dosyadaki karşılık gelen satıra erişmesi sağlanır. .

Tüm dosya tanıtıcısı içeriğini bir skaler olarak okuyabilirsiniz.

{
local(*INPUT, $/);
open (INPUT, $file)     || die "can't open $file: $!";
$var = <INPUT>;
}

Bu, kayıt ayırıcınızı geçici olarak geri alır ve blok çıkışında dosyayı otomatik olarak kapatır. Dosya zaten açıksa, şunu kullanın:

$var = do { local $/; <INPUT> };

Sıradan dosyalar için okuma işlevini de kullanabilirsiniz.

read( INPUT, $var, -s INPUT );

Üçüncü argüman, INPUT dosya tanıtıcısı üzerindeki verinin bayt boyutunu test eder ve bu kadar baytı arabelleğe $ var okur.


8

Basit bir yol:

while (<FILE>) { $document .= $_ }

Başka bir yol da girdi kaydı ayırıcısını "$ /" değiştirmektir. Global kayıt ayırıcısını değiştirmekten kaçınmak için bunu yerel olarak çıplak bir blokta yapabilirsiniz.

{
    open(F, "filename");
    local $/ = undef;
    $d = <F>;
}

1
Verdiğiniz her iki örnekte de önemli sayıda sorun var. Asıl sorun, eski Perl ile yazılmış olmalarıdır, Modern Perl
Brad Gilbert

@Brad, yorum yıllar önce yapıldı, ancak mesele hala duruyor. daha iyisi{local $/; open(my $f, '<', 'filename'); $d = <$f>;}
Joel Berger

@Joel bu sadece biraz daha iyi. openÇıktısını veya dolaylı olarak çağrılanı kontrol etmediniz close. my $d = do{ local $/; open(my $f, '<', 'filename') or die $!; my $tmp = <$f>; close $f or die $!; $tmp}. (Bunda hala giriş kodlamasını belirtmemesi sorunu var.)
Brad Gilbert

use autodie, göstermek istediğim en büyük gelişme sözcüksel dosya tanıtıcısı ve 3 argüman açıktı. Bunu doyapmanın bir sebebi var mı ? neden dosyayı bloktan önce bildirilen bir değişkene dökmüyorsunuz?
Joel Berger

7

Ya kümesi $/için undef(jrockway en cevaba bakınız) ya da sadece tüm dosyanın satırları bitiştirmek:

$content = join('', <$fh>);

Destekleyen herhangi bir Perl sürümünde dosya tanıtıcıları için skalar kullanmanız önerilir.


4

Başka bir olası yol:

open my $fh, '<', "filename";
read $fh, my $string, -s $fh;
close $fh;

3

Elmas operatöründen yalnızca ilk satırı alıyorsunuz <FILE>çünkü onu skaler bağlamda değerlendiriyorsunuz:

$document = <FILE>; 

Liste / dizi bağlamında, elmas operatörü dosyanın tüm satırlarını döndürür.

@lines = <FILE>;
print @lines;

1
Sadece isimlendirme üzerine bir not: uzay gemisi operatörü <=>ve <>elmas operatörüdür.
toolic

Oh, teşekkürler, daha önce "elmas operatörü" nü duymamıştım ve ikisinin de aynı adı paylaştığını düşünmüştüm. Yukarıda düzelteceğim.
Nathan

2

Bunu en basit şekilde yapardım, böylece daha akıllı yollar olsa bile herkes ne olduğunu anlayabilir:

my $text = "";
while (my $line = <FILE>) {
    $text .= $line;
}

Tüm bu dizi birleştirme işlemleri oldukça pahalı olacak. Bunu yapmaktan kaçınırdım. Verileri neden yalnızca bir araya getirmek için parçalara ayıralım?
andru

2
open f, "test.txt"
$file = join '', <f>

<f>- dosyamızdan bir satır dizisi döndürür (eğer $/varsayılan değere sahipse "\n") ve sonra join ''bu diziyi içine yapıştırır.


2

Bu daha çok nasıl YAPILMAMASI gerektiğine dair bir öneridir . Oldukça büyük bir Perl uygulamasında bir hata bulmakta kötü bir zaman geçirdim. Modüllerin çoğunun kendi yapılandırma dosyaları vardı. Yapılandırma dosyalarını bir bütün olarak okumak için, Perl'in bu tek satırını internette bir yerde buldum:

# Bad! Don't do that!
my $content = do{local(@ARGV,$/)=$filename;<>};

Satır ayırıcıyı daha önce açıklandığı gibi yeniden atar. Ama aynı zamanda STDIN'i de yeniden atar.

Bu, bulmam saatler süren en az bir yan etkiye sahipti: Örtülü dosya tanıtıcısını düzgün şekilde kapatmıyor (çünkü close ).

Örneğin, bunu yapmak:

use strict;
use warnings;

my $filename = 'some-file.txt';

my $content = do{local(@ARGV,$/)=$filename;<>};
my $content2 = do{local(@ARGV,$/)=$filename;<>};
my $content3 = do{local(@ARGV,$/)=$filename;<>};

print "After reading a file 3 times redirecting to STDIN: $.\n";

open (FILE, "<", $filename) or die $!;

print "After opening a file using dedicated file handle: $.\n";

while (<FILE>) {
    print "read line: $.\n";
}

print "before close: $.\n";
close FILE;
print "after close: $.\n";

sonuçlanır:

After reading a file 3 times redirecting to STDIN: 3
After opening a file using dedicated file handle: 3
read line: 1
read line: 2
(...)
read line: 46
before close: 46
after close: 0

Garip olan, satır sayacının $.her dosya için birer birer artırılmasıdır. Sıfırlanmaz ve satır sayısını içermez. Ve başka bir dosyayı açarken en az bir satır okunana kadar sıfırlanmaz. Benim durumumda şöyle bir şey yapıyordum:

while($. < $skipLines) {<FILE>};

Bu problem nedeniyle, hat sayacı doğru şekilde sıfırlanmadığı için koşul yanlıştı. Bunun bir hata mı yoksa sadece yanlış kod mu olduğunu bilmiyorum ... Ayrıca oder'i close;aramak close STDIN;da yardımcı olmuyor.

Bu okunamayan kodu open, string concatenation ve close kullanarak değiştirdim. Bununla birlikte, Brad Gilbert tarafından yayınlanan çözüm, bunun yerine açık bir dosya tanıtıcısı kullandığı için de işe yarıyor.

Baştaki üç satır şu şekilde değiştirilebilir:

my $content = do{local $/; open(my $f1, '<', $filename) or die $!; my $tmp1 = <$f1>; close $f1 or die $!; $tmp1};
my $content2 = do{local $/; open(my $f2, '<', $filename) or die $!; my $tmp2 = <$f2>; close $f2 or die $!; $tmp2};
my $content3 = do{local $/; open(my $f3, '<', $filename) or die $!; my $tmp3 = <$f3>; close $f3 or die $!; $tmp3};

dosya tutamacını düzgün kapatır.


2

kullanım

 $/ = undef;

önce $document = <FILE>;. $/olan giriş kayıt ayırıcı varsayılan olarak satır olduğunu. Bunu olarak yeniden tanımlayarak undef, alan ayırıcı olmadığını söylüyorsunuz. Buna "slurp" modu denir.

Gibi undef $/ve local $/(ama değil my $/) diğer çözümler $ / 'ı yeniden beyan eder ve böylece aynı etkiyi üretir.


0

Basitçe bir alt rutin oluşturabilirsiniz:

#Get File Contents
sub gfc
{
    open FC, @_[0];
    join '', <FC>;
}

0

İyi bir uygulama mı bilmiyorum ama bunu kullanırdım:

($a=<F>);

-1

Bunların hepsi güzel cevaplar. AMA, tembel hissediyorsanız ve dosya o kadar büyük değilse ve güvenlik bir sorun değilse (bozuk bir dosya adınız olmadığını biliyorsunuz), o zaman şunları yapabilirsiniz:

$x=`cat /tmp/foo`;    # note backticks, qw"cat ..." also works

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.