2009-12-12 23 views
9

Lập trình rất mới đối với tôi mà tôi xin lỗi vì không biết cách đặt câu hỏi.Làm cách nào tôi có thể chia nhỏ các cột có chiều rộng cố định trong Perl?

Tôi có một tập lệnh Perl được biến từ một công cụ nội bộ. Đây không phải lúc nào cũng giống như vậy, nhưng nó sẽ luôn luôn theo mẫu này:

darren.local   1987 A  Sentence1 
darren.local   1996 C  Sentence2 
darren.local   1991 E  Sentence3 
darren.local   1954 G  Sentence4 
darren.local   1998 H  Sentence5 

Với Perl, cách dễ nhất để biến từng dòng này thành biến là gì? Tùy thuộc vào những gì công cụ nội bộ spits ra mỗi dòng sẽ luôn luôn khác nhau và có thể có nhiều hơn năm dòng. Các chữ cái viết hoa trong mỗi dòng là những gì nó sẽ kết thúc được sắp xếp theo (tất cả Như, tất cả Cs, tất cả Es, vv). Tôi có nên nhìn vào các biểu thức chính quy không?

+0

các dữ liệu/dòng này ở đâu? Công cụ nội bộ của bạn có đưa chúng vào một biến duy nhất không? Hoặc là dữ liệu văn bản này trong một tệp bạn cần đọc? –

+0

Công cụ đặt chúng vào một biến duy nhất. – scraft3613

+0

Có người mới Perl !!! 1 – nes1983

Trả lời

17

Tôi thích sử dụng unpack cho loại điều này. Nó nhanh, linh hoạt và có thể đảo ngược.

Bạn chỉ cần biết vị trí cho từng cột và unpack có thể tự động cắt khoảng trắng thừa từ mỗi cột.

Nếu bạn thay đổi điều gì đó trong một trong các cột, thật dễ dàng để đi gói sang định dạng ban đầu bằng cách đóng gói lại với định dạng tương tự:

my $format = 'A23 A8 A7 A*'; 

while(<DATA>) { 
    chomp(my $line = $_); 

    my($machine, $year, $letter, $sentence) = 
     unpack($format, $_); 

    # save the original line too, which might be useful later 
    push @grades, [ $machine, $year, $letter, $sentence, $_ ]; 
    } 

my @sorted = sort { $a->[2] cmp $b->[2] } @grades; 

foreach my $tuple (@sorted) { 
    print $tuple->[-1]; 
    } 

# go the other way, especially if you changed things 
foreach my $tuple (@sorted) { 
    print pack($format, @$tuple[0..3]), "\n"; 
    } 

__END__ 
darren.local   1987 A  Sentence1 
darren.local   1996 C  Sentence2 
darren.local   1991 E  Sentence3 
darren.local   1954 G  Sentence4 
darren.local   1998 H  Sentence5 

Bây giờ, có một yếu tố bổ sung. Có vẻ như bạn có thể có đoạn văn bản nhiều dòng này trong một biến duy nhất. Xử lý này như bạn sẽ một tập tin bằng cách mở một filehandle trên một tham chiếu đến vô hướng. Những thứ filehandle chăm sóc phần còn lại:

my $lines = '...multiline string...'; 

open my($fh), '<', \ $lines; 

while(<$fh>) { 
     ... same as before ... 
     } 
+1

Định dạng của '' A23 A8 A7 A * ''cũng sẽ hoạt động. –

+3

Một ví dụ điển hình về Perl có thể đọc được ... (thậm chí với người dùng một lần-mỗi-hai-năm) – Rook

+0

Tôi không chắc chắn bạn đã thấy định dạng nào vì tôi đã mắc lỗi trong lần đầu tiên tôi đăng, nhưng chúng tôi đã kết thúc ở cùng một định dạng. –

3
use strict; 
use warnings; 

# this puts each line in the array @lines 
my @lines = <DATA>; # <DATA> is a special filehandle that treats 
        # everything after __END__ as if it was a file 
        # It's handy for testing things 

# Iterate over the array of lines and for each iteration 
# put that line into the variable $line 
foreach my $line (@lines) { 
    # Use split to 'split' each $line with the regular expression /s+/ 
    # /s+/ means match one or more white spaces. 
    # the 4 means that all whitespaces after the 4:th will be ignored 
    # as a separator and be included in $col4 
    my ($col1, $col2, $col3, $col4) = split(/\s+/, $line, 4); 

    # here you can do whatever you need to with the data 
    # in the columns. I just print them out 
    print "$col1, $col2, $col3, $col4 \n"; 
} 


__END__ 
darren.local   1987 A  Sentece1 
darren.local   1996 C  Sentece2 
darren.local   1991 E  Sentece3 
darren.local   1954 G  Sentece4 
darren.local   1998 H  Sentece5 
0

Đối với mỗi dòng của một cái gì đó văn bản như thế này:

my ($domain, $year, $grade, @text) = split /\s+/, $line; 

tôi sử dụng một mảng cho câu vì nó không rõ ràng nếu câu ở cuối sẽ có các khoảng trống hay không. sau đó bạn có thể tham gia mảng @text thành chuỗi mới nếu cần. Nếu các câu ở cuối sẽ không có dấu cách thì bạn có thể biến @text thành $ text.

+0

Nếu bạn định sử dụng phân tách trong trường hợp này, hãy sử dụng đối số thứ ba để giới hạn số phần tử nó trả về. Nếu cột cuối cùng đó có khoảng trắng đáng kể, bạn sẽ mất một phần dữ liệu. –

2

Giả sử rằng các văn bản được đưa vào một biến thông tin $ duy nhất, sau đó bạn có thể chia nó thành dòng riêng biệt bằng cách sử dụng chức năng perl chia nội tại:

my @lines = split("\n", $info); 

nơi @lines là một mảng của đường dây của bạn. "\ N" là regex cho một dòng mới. Bạn có thể lặp qua mỗi dòng như sau:

foreach (@lines) { 
    $line = $_; 
    # do something with $line.... 
} 

Sau đó bạn có thể chia nhỏ mỗi dòng trên khoảng trắng (regex \ s +, nơi \ s là một trong những nhân vật khoảng trắng, và + có nghĩa là 1 hoặc nhiều lần):

@fields = split("\s+", $line); 

và sau đó bạn có thể truy cập vào từng lĩnh vực trực tiếp thông qua chỉ số mảng của nó: $ lĩnh vực [0], $ field [1], vv

hay, bạn có thể làm:

($var1, $var2, $var3, $var4) = split("\s+", $line); 

sẽ đặt các trường trong mỗi dòng vào các biến được đặt tên riêng biệt.

Bây giờ - nếu bạn muốn loại dòng của bạn bằng cách nhân vật trong cột thứ ba, bạn có thể làm điều này:

my @lines = split("\n", $info); 
my @arr =(); # declare new array 

foreach (@lines) { 
    my @fields = split("\s+", $_); 
    push(@arr, \@fields) # add @fields REFERENCE to @arr 
} 

Bây giờ bạn có một "mảng của mảng". Điều này có thể dễ dàng được sắp xếp như sau:

@sorted = sort { $a->[2] <=> $b->[2] } @arr; 

sẽ sắp xếp @arr theo phần tử thứ 3 (chỉ số 2) của @fields.

Sửa 2 Để đưa dòng với cột thứ ba cùng vào biến riêng của họ, làm như sau:

my %hash =();    # declare new hash 

foreach $line (@arr) {  # loop through lines 
    my @fields = @$line;  # deference the field array 

    my $el = $fields[2];  # get our key - the character in the third column 

    my $val = ""; 
    if (exists $hash { $el }) {   # check if key already in hash 
    my $val = $hash{ $el };  # get the current value for key 
    $val = $val . "\n" . $line; # append new line to hash value   
    } else { 
    $val = $line; 
    } 
    $hash{ $el } = $val;   # put the new value (back) into the hash 
} 

Bây giờ bạn có một băm keyed với các nhân vật cột thứ ba, với các giá trị cho mỗi con người chủ chốt các dòng có chứa khóa đó. Sau đó, bạn có thể lặp qua băm và in ra hoặc sử dụng các giá trị băm khác.

+0

Nếu bạn định sử dụng phân tách trong trường hợp này, hãy sử dụng đối số thứ ba để giới hạn số phần tử mà nó trả về. Nếu cột cuối cùng đó có khoảng trắng đáng kể, bạn sẽ mất một phần dữ liệu. –

+0

Cảm ơn Richard - mỗi dòng cần được nhóm theo các chữ cái viết hoa. Tùy thuộc vào đầu ra của truy vấn đó tôi có thể có tới 20 dòng hoặc ít nhất là 2 dòng. Các dòng có "C" cần phải đi vào một biến, các dòng với "B" cần phải đi vào biến riêng của chúng, v.v. Điều đó có hiệu quả không? – scraft3613

+0

sử dụng chức năng sắp xếp trong câu trả lời của tôi ở trên, mảng của bạn sẽ được sắp xếp theo thứ tự chữ và số. Vì vậy, "A" s sẽ xuất hiện đầu tiên, "B" của tiếp theo và như vậy. Nếu bạn muốn đặt tất cả các "A" dòng vào một biến duy nhất, có (giống như bất kỳ vấn đề lập trình) một số khả năng. Bạn có thể sử dụng băm/bản đồ có khóa, với các ký tự "A" vv làm khóa của bạn, với giá trị là a) một mảng các dòng hoặc b) một đĩa đơn mà bạn nối thêm các dòng tiếp theo khi bạn tìm thấy chúng. Xem here để biết hướng dẫn về cách sử dụng băm. –

-1

Sử dụng CPAN, và mô-đun của tôi DataExtract::FixedWidth

#!/usr/bin/env perl 
use strict; 
use warnings; 
use DataExtract::FixedWidth; 

my @rows = <DATA>; 

my $defw = DataExtract::FixedWidth->new({ heuristic => \@rows, header_row => undef }); 

use Data::Dumper; 

print Dumper $defw->parse($_) for @rows; 

__DATA__ 
darren.local   1987 A  Sentence1 
darren.local   1996 C  Sentence2 
darren.local   1991 E  Sentence3 
darren.local   1954 G  Sentence4 
darren.local   1998 H  Sentence5 

Không có được đơn giản hơn nhiều hơn thế.

Các vấn đề liên quan