Classifique o arquivo de entrada pelos resultados de um regex

7

Gostaria de classificar um arquivo com base nos resultados de uma expressão regex. Por exemplo, se eu tenho as seguintes declarações de propriedade em 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

Por padrão, eles ordenarão na ordem [4, 1, 2, 3], mas eu gostaria de classificá-los na ordem dos nomes das propriedades, [1, 3, 2, 4]. Eu posso escrever uma expressão regular para extrair apenas o nome da propriedade, é possível classificar os resultados dessa expressão?

Existe alguma ferramenta interna do Unix que faça isso para mim? Eu estou trabalhando no Xcode, então as soluções VIM / emacs não vão ajudar.

Além disso, a razão pela qual eu gostaria de fazer isso usando um regex é para que eu possa expandir meu algoritmo de classificação para trabalhar em outras situações. Use-o para classificar declarações de métodos, declarações de importação, etc.

    
por kubi 13.08.2012 / 21:01

5 respostas

11

Um método geral para classificar por uma função arbitrária do conteúdo da linha é o seguinte:

  1. Obtenha a chave pela qual você deseja classificar e copie-a para o início da linha
  2. Classificar
  3. Exclua a chave do começo da linha

Aqui está uma chave que você pode usar neste caso em particular: este programa sed produzirá a linha do último identificador até o final.

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

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

Para colocar essas chaves e as linhas originais lado a lado:

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

Para ordená-los ...

| sort

e deixar apenas o segundo campo (a linha original)

| cut -f 2-

Todos juntos (ordenando em ordem inversa, então há algo para mostrar):

% paste <(sed -e 's/^.*[^[:alnum:]_]\([[:alpha:]][[:alnum:]_]*\)//' < 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
    
por 13.08.2012 / 21:26
2
PIPED-DATA | sed -r "s/(\*\w+)/\x01&\x01/" | sort -k2 -t$'\x01' |tr -d $'\x01'

O script acima é suficiente para a sua situação. Na verdade, é basicamente o suficiente para qualquer tipo de campo de chave única. Para o mesmo script, expandido, continue a ler.

O script a seguir configura o campo para ser classificado como 2 , mas o layout do campo é bastante flexível. Você pode classificar em vários campos, se necessário, especificando padrões de regex apropriados e alterando as opções de classificação de acordo.

Cada padrão de campo deve ser agrupado em% normal( colchetes ) e 'single-quoted' .

Os padrões que você fornece são delimitados por qualquer caractere exclusivo que você escolher. sed também precisa de um delimitador único. O script usa os delimitadores \x01 e \x02 . Esses valores delimitadores foram escolhidos porque eles não aparecem normalmente nos arquivos de texto.

Note que sua configuração deve ser considerada como baseada na composição do campo, não por delimitadores de campo.

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"

Saída:

@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
    
por 14.08.2012 / 00:52
0
sort -k 5 ~/Temp/data

funcionou para mim no Cygwin.

    
por 13.08.2012 / 21:11
0

Isso usa Python. A sintaxe dos Pythons não é boa para os one-liners, exceto que o bash shell manipulará duas linhas e o código poderá usar aspas duplas para suas constantes de string: -)

As rotinas de classificação Pythons permitem que você use uma função lambda para extrair a chave para as linhas a serem ordenadas (a decoração, ordenação, desdotação dos outros métodos).

O regexp que eu uso apenas extrai a palavra não espacial após a primeira subcadeia '*' nas linhas.

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$ 
    
por 27.08.2012 / 09:13
0

Eu criei um script perl para fazer exatamente isso, você pode inserir um regex para classificar um arquivo pela primeira captura. então você pode definir um sinalizador para fazer uma string ou comparação numérica. basta jogar esse exemplo de código em um arquivo .pl.

é bem simples e a lógica realmente fica nas linhas 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);
    }
}
    
por 05.02.2016 / 17:54