Sắp xếp tệp đầu vào theo kết quả của biểu thức chính quy


7

Tôi muốn sắp xếp một tệp dựa trên kết quả của biểu thức regex. Ví dụ: nếu tôi có các khai báo thuộc tính sau trong Obj-C

@property (nonatomic, strong) id <AlbumArtDelegate, UITextFieldDelegate> *albumArtView; // 1
@property (nonatomic, strong, readonly) UIImageView *profileView;  // 2
@property (nonatomic, strong, readwrite) UIButton *postFB;          // 3
@property (nonatomic, assign) UIButton *saveButton;      // 4

Theo mặc định, họ sẽ sắp xếp theo thứ tự [4, 1, 2, 3], nhưng tôi muốn sắp xếp chúng theo thứ tự tên tài sản thực tế, [1, 3, 2, 4]. Tôi có thể viết một biểu thức chính quy để chỉ trêu chọc tên thuộc tính, tôi có thể sắp xếp theo kết quả của biểu thức đó không?

Có công cụ Unix tích hợp nào sẽ làm điều này cho tôi không? Tôi đang làm việc trong Xcode, vì vậy các giải pháp VIM / emacs sẽ không giúp ích.

Ngoài ra, lý do tôi muốn làm điều này bằng regex là để tôi có thể mở rộng thuật toán sắp xếp của mình để hoạt động trong các tình huống khác. Sử dụng nó để sắp xếp khai báo phương thức, báo cáo nhập khẩu, v.v.


1
sort -k 5không làm việc
cjc

Tôi không quen thuộc với sắp xếp và cờ của nó, nhưng tôi khá chắc chắn rằng nó sẽ không hoạt động. Có sự khác biệt lớn về số lượng vật phẩm giữa đầu dòng và tên của tài sản. Đó là 5 trong ví dụ này, nhưng nó có thể là 3 hoặc hơn 7.
kubi

Vậy, định dạng thực tế là gì? Trường bạn quan tâm để sắp xếp nằm ở cột cuối cùng (ngoại trừ bạn có thể có // nhận xét)?
cjc

Ngoài ra, xem phần cuối của câu hỏi của tôi. Bất cứ giải pháp nào tôi nhận được, tôi muốn có thể sử dụng nó để sắp xếp phức tạp hơn nhiều so với các ví dụ tôi đã đưa ra ở đây. Giống như khai báo phương pháp sắp xếp. Trong trường hợp đó, giá trị tôi muốn sắp xếp sẽ nằm ở giữa khóa đầu tiên.
kubi

Câu trả lời:


11

Một phương pháp chung để sắp xếp theo một hàm tùy ý của nội dung của dòng như sau:

  1. Lấy khóa bạn muốn sắp xếp theo và sao chép nó vào đầu dòng
  2. Sắp xếp
  3. Xóa khóa từ đầu dòng

Đây là một khóa bạn có thể sử dụng trong trường hợp cụ thể này: sedchương trình này sẽ xuất dòng từ định danh cuối cùng đến cuối.

% sed -e 's/^.*[^[:alnum:]_]\([[:alpha:]][[:alnum:]_]*\)/\1/' < decls

albumArtView; // 1
profileView;  // 2
postFB;          // 3
saveButton;      // 4

Để đặt các phím này và các dòng gốc cạnh nhau:

% paste <(sed -e 's/^.*[^[:alnum:]_]\([[:alpha:]][[:alnum:]_]*\)/\1/' < decls) decls

Để sắp xếp chúng ...

| sort

và chỉ để lại trường thứ hai (dòng ban đầu)

| cut -f 2-

Tất cả cùng nhau (sắp xếp theo thứ tự ngược lại, vì vậy có một cái gì đó để hiển thị):

% paste <(sed -e 's/^.*[^[:alnum:]_]\([[:alpha:]][[:alnum:]_]*\)/\1/' < decls) decls \
  | sort -r \
  | cut -f 2-

@property (nonatomic, assign) UIButton *saveButton;      // 4
@property (nonatomic, strong, readonly) UIImageView *profileView;  // 2
@property (nonatomic, strong, readwrite) UIButton *postFB;          // 3
@property (nonatomic, strong) id <AlbumArtDelegate, UITextFieldDelegate> *albumArtView; // 1

2
Chỉ cần một lưu ý b / c OP có thể sẽ muốn điều chỉnh phương pháp này. Bằng cách sử dụng \1 &trong biểu thức thay thế của lệnh sed thay thế, bạn có thể bỏ qua declstệp tạm thời và pastelời gọi. Ngoài ra, regex sẽ cần được điều chỉnh để chỉ khớp với mã định danh (ví dụ đưa ra sẽ không phù hợp nếu một nhận xét ở cuối dòng chứa bất kỳ số nhận dạng nào không có số).
jw013

Cảm ơn, @angus!. Có tính đến nhận xét của @ jw013, tôi chắc chắn rằng nó sẽ hoạt động tốt cho những gì tôi muốn.
kubi

3
@ jw013: Vâng, tôi nghĩ rằng anh ấy có thể muốn sử dụng một công cụ khác để lấy chìa khóa - có thể anh ấy biết perl hoặc awk. Trong trường hợp đó, sử dụng pastelà giải pháp chung "mẫu số nhỏ nhất". Btw, đó phải là một tab giữa \1&(không thể phân biệt từ đây).
angus

@kubi rất vui khi được giúp đỡ. :)
angus

Cuối cùng tôi đã không làm điều chính xác này, mà thay vào đó, đặt một tập lệnh ruby ​​trong Dịch vụ Automator.app. jimkubicek.com/blog/2012/09/20/sort-methods-with-automator
kubi

2
PIPED-DATA | sed -r "s/(\*\w+)/\x01&\x01/" | sort -k2 -t$'\x01' |tr -d $'\x01'

Kịch bản trên là đủ cho tình huống của bạn. Trên thực tế, nó về cơ bản là đủ cho bất kỳ loại khóa đơn trường nào .. Đối với cùng một tập lệnh, được mở rộng, hãy đọc tiếp.


Kịch bản sau đây thiết lập trường được sắp xếp thành 2 , nhưng bố cục trường khá linh hoạt. Bạn có thể sắp xếp trên nhiều trường, nếu bạn cần, bằng cách chỉ định các mẫu biểu thức chính quy phù hợp và thay đổi các tùy chọn sắp xếp cho phù hợp.

Mỗi mẫu trường nên được bọc trong (dấu ngoặc bình thường )'single-quoted'.

Các mẫu mà bạn cung cấp được phân định bởi bất kỳ ký tự duy nhất nào bạn chọn. sedcũng cần một dấu phân cách độc đáo. Kịch bản sử dụng dấu phân cách \x01\x02. Các giá trị dấu phân cách này đã được chọn vì chúng không xuất hiện trong tệp văn bản.

Lưu ý rằng thiết lập của bạn phải được coi là dựa trên composiiton trường, không phải bởi các dấu phân cách trường ..

n=2                                  # field number to sort on
p=( '(.*)'  '(\*\w+)'  '(.*)' )      # Set up regex field patterns

f=; r=; d=$'\x01';  x=$'\x02'        # Build patterns and delimiters
for (( i=0; i<${#p[@]}; i++ )) ;do 
   f+="${p[i]}"; r+="\\$((i+1))$x"
done

sed -r "s$d$f$d$r$d" file |sort -k$n -t"$x" |tr -d  "$x"

Đầu ra:

@property (nonatomic, strong) id <AlbumArtDelegate, UITextFieldDelegate> *albumArtView; // 1
@property (nonatomic, strong, readwrite) UIButton *postFB;          // 3
@property (nonatomic, strong, readonly) UIImageView *profileView;  // 2
@property (nonatomic, assign) UIButton *saveButton;      // 4

1
sort -k 5 ~/Temp/data

làm việc cho tôi trên Cygwin.


Cập nhật câu hỏi của tôi với một ví dụ khó hơn (và thực tế)
kubi

1
sort -k 5n ~/Temp/datađể sắp xếp số theo trường 5.
Petr Javorik

0

Cái này sử dụng Python. Cú pháp của Pythons không tốt cho một lớp, ngoại trừ bash shell sẽ vui vẻ xử lý hai dòng và mã có thể sử dụng dấu ngoặc kép cho các hằng chuỗi của nó :-)

Thói quen sắp xếp của Pythons cho phép bạn sử dụng hàm lambda để trích xuất khóa cho các dòng được sắp xếp trên (trang trí, sắp xếp, không loại bỏ các phương thức khác).

Regrec tôi sử dụng chỉ trích xuất từ ​​không phải dấu cách sau chuỗi con '*' đầu tiên trong các dòng.

paddy$ python -c 'import sys, re
print ("\n".join(sorted((line.rstrip() for line in sys.stdin), key=lambda x: re.search(r"\s[*](\S+)", x).group(1))))' < test_in2.txt 
(nonatomic, strong) id <AlbumArtDelegate, UITextFieldDelegate> *albumArtView; // 1
@property (nonatomic, strong, readwrite) UIButton *postFB;          // 3
@property (nonatomic, strong, readonly) UIImageView *profileView;  // 2
@property (nonatomic, assign) UIButton *saveButton;      // 4
paddy$ 

Nếu bạn thích sử dụng Python theo cách này, tôi khuyên bạn nên pythonpy. Với nó, mã của bạn có thể được viết là py -l 'sorted(l, key=lambda x: re.search(r"\s[*](\S+)", x).group(1)) '< test_in2.txt, nếu tôi không làm hỏng dấu ngoặc đơn.
GingerPlusPlus

Cảm ơn Ginger, nhưng không cảm ơn. Tôi khá vui khi sử dụng awk / Perl / sed cho một lớp lót mở rộng nếu tôi phải, nhưng thích sử dụng Python không bị biến đổi.
Paddy3118

0

Tôi đã tạo một tập lệnh perl để thực hiện chính xác điều này, bạn có thể nhập biểu thức chính quy để sắp xếp tệp bằng cách chụp đầu tiên. sau đó bạn có thể đặt cờ để thực hiện so sánh chuỗi hoặc số. chỉ cần ném mẫu mã này vào tệp .pl.

nó khá đơn giản và logic thực sự chỉ nằm trên các dòng 20-37.

#! /usr/bin/perl
# Created by pete Nixon

use Getopt::Long;
use strict;
use Cwd qw(abs_path);

my $exec_path = abs_path($0);
   $exec_path =~ s/(.*\x2f)[^\x2f]+$/$1/g;
my $path = abs_path($1);

&getCommandLineArguments;

my $file_flag;
my $regex;
my $type_flag;
my @lines;
my @sortedLines;

open (FILE, $file_flag) || die "Couldn't open rule file, $!";
while (<FILE>) {
    chomp $_;
    if ($_ =~ /^\s*\n/) {
        next;
    }
    push (@lines, $_);
}

if ($type_flag eq 1) {
    @sortedLines = sort { ($a =~ m/$regex/)[0] <=> ($b =~ m/$regex/)[0]} @lines; # where the magic happens
} else {
    @sortedLines = sort { ($a =~ m/$regex/)[0] cmp ($b =~ m/$regex/)[0]} @lines; # where the magic happens
}

foreach (@sortedLines) {
    print "$_\n";
}

sub getCommandLineArguments() {
    my $help;
    my $clear = "[0m";
    my $black = "[0;30m";
    my $blue = "[0;34m";
    my $green = "[0;32m";
    my $cyan = "[0;36m";
    my $red = "[0;31m";
    my $purple = "[0;35m";
    my $brown = "[0;33m";
    my $gray = "[0;37m";
    my $darkGray = "[1;30m";
    my $lightBlue = "[1;34m";
    my $lightGreen = "[1;32m";
    my $lightCyan = "[1;36m";
    my $lightRed = "[1;31m";
    my $lightPurple = "[1;35m";
    my $yellow = "[1;33m";
    my $white = "[1;37m";
    GetOptions (
        'file|f=s' =>   \$file_flag,
        'regex|r=s' => \$regex,
        'type|t=s' => \$type_flag,
        'help|h|?' => \$help
        ) or die ("Error in command line arguments$clear\n");
    if ($help || $file_flag eq undef && $regex eq undef) {
        print "$green================================================================================$clear\n";
        print "$red WHAT DOES THIS SCRIPT DO?\n$clear";
        print "$cyan    - This program a regex and sorts a line based on it.\n$clear";
        print "$red HOW DO I USE THIS SCRIPT?\n$clear";
        print "$cyan    - Type the name of this script, space, options (see the next section)\n$clear";
        print "$green   SAMPLE: '$clear" . "sortbyregex.pl -f file -r \"regex\" -t (1|2)$green'\n$clear";
        print "$red WHAT OPTIONS ARE AVAILABLE?\n$clear";
        print "$yellow  -f, --file\n$clear";
        print "$cyan    - Use to specify a regex\n$clear";
        print "$yellow  -r, --regex\n$clear";
        print "$cyan    - Use to specify the regex used for sorting, must include one capture\n$clear";
        print "$yellow  -t, --type\n$clear";
        print "$cyan    - Use to specify the type of sorting 1 = numeric 2 = string\n$clear";
        print "$yellow  -h, --help, -?\n$clear";
        print "$cyan    - Use to see this help... so... yeah...\n$clear";
        print "$green================================================================================$clear\n";
        exit(0);
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.