как убрать пробелы между значениями int в файле? - PullRequest
4 голосов
/ 28 февраля 2020

Учитывая файл, содержащий два столбца целых чисел, я хочу избавиться от пробелов между целочисленными значениями. Под пробелом я подразумеваю, что если мы возьмем два целых числа A и B, таким образом, что C, например, A

1 2
1 3
2 5
6 9
3 5
7 9
11 6
7 11

, к этому нет:

1 2 
1 3
2 4
5 7
3 4
6 7
8 5
6 8

В первых двух столбцах настоящие целые числа {1,2,3,5,6,7,9,11}. Отсутствуют значения {4,8,10}. цель состоит в том, чтобы уменьшить каждое целое число на количество пропущенных значений, которые меньше его. поэтому 5,6 и 7 уменьшаются на 1, 9 нас уменьшаются на 2, а 11 уменьшаются на 3. таким образом, значения {1,2,3,5,6,7,9,11} заменяются на {1, 2,3,4,5,6,7,8}. Кто-нибудь знает, как это сделать эффективно, используя команду linux, сценарий bash или команду awk? Спасибо!

Редактировать: я пытался сделать это, но я не нашел способа сделать это в сценарии оболочки, мне пришлось написать программу c, которая выполняет сценарии оболочки. первая часть просто сортирует файл, вторая делает то, о чем я говорил в вопросе.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>


#define MAX_INTS 100000000

void process_file(char *path){
    //FIRST PART
    char *outfpath="tmpfile";
    char *command=calloc(456+3*strlen(path)+strlen(outfpath),sizeof(char));

    sprintf(command,"#!/bin/bash \nvar1=$( cat %s | head -n 4  && ( cat %s | tail -n +5  | awk '{split( $0, a, \" \" ); asort( a ); for( i = 1; i <= length(a); i++ ) printf( \"%c%c \", a[i] ); printf( \"\\n\" ); }' | sort -n -k1,1 -k2 | uniq) )\nvar2=$( ( (echo \"$var1\" | tail -n +5 | cut -f 1 -d\" \") && (echo \"$var1\" | tail -n +5 | cut -f 2 -d\" \" ) ) | sort -n -k1,1 | uniq | awk '{for(i=p+1; i<$1; i++) print i} {p=$1}' )\necho \"$var1\" > %s\necho \"$var2\"| tr \"\\n\" \" \" > %s",path,path,'%','s',path,outfpath);

    if(system(command)==-1){
        fprintf(stderr,"Erreur à l'exécution de la commande \n%s\n",command);
    }
    //the first part only sorts the file and puts in outpath the list of the missing integers

    //SECOND PART
    long unsigned start=0,end=0,val,index=0;
    long unsigned *intvals=calloc(MAX_INTS,sizeof(long unsigned));
    FILE *f=fopen(outfpath,"r");

    //reads the files and loads the missing ints to the array intvals
    while(fscanf(f,"%lu ",&val)==1){
        end=index;
        intvals[index]=val;
        index++;
    }
    if (index==0) return;
    intvals=realloc(intvals,index*sizeof(long unsigned));
    fclose(f);
    free(command);


    f=fopen(path,"r+w");
    char *line=calloc(1000,sizeof(char));
    command=calloc(1000,sizeof(char));
    char *str;
    long unsigned v1,v2,
        d1=0,d2=0,
        c=0,prec=-1,start_l=0;
    int pos1, pos2;  

    //read a file containing two columns of ints 
    //for each pair v1 v2, count d1 d2, 
    //such as d1 is the number of missing values smaller than v1, d2 the number of missing values smaller than v2
    //and overrwrite the line in the file using sed with the values v1-d1 and v2-d2

    while(fgets(line,1000,f)!=NULL && line[0]=='#'){ continue; }

    do{
        str=strtok(line," \t");
        v1=atoi(str);
        str=strtok(NULL," \t");
        v2=atoi(str);
        if(prec!=v1) {
            prec=v1;
            d2=d1;
            start_l=start;
        }
        for(index=start;index<=end;index++){ 
            if(intvals[index]<v1){ 
                 d1++; 
                 start++;
                 c=1;
            }else{
                start=d1;
                break;
            }
        }
        for(index=start_l;index<=end;index++){ 
            if(intvals[index]<v2){ 
                d2++; 
                start_l++;
                c=1; 
            }else{ 
                break;
            }
        }         
        if(c){
            sprintf(command,"sed -i 's/%lu %lu/%lu %lu/' %s",v1,v2,v1-d1,v2-d2,path);
            if(system(command)==-1){
                fprintf(stderr,"Erreur à l'exécution de la commande \n%s\n",command);
            }
        }
        c=0;
    }while(fgets(line,1000,f)!=NULL);
    fclose(f);
    free(command);
    free(line);
    free(intvals);
}



int main(int argc,char* argv[]){

    process_file(argv[1]);
    return 0;
}    

Ответы [ 3 ]

4 голосов
/ 28 февраля 2020

Это может сделать это:

awk '(NR==FNR){for(i=1;i<=NF;++i) {a[$i]; max=(max<$i?$i:max)};next}
     (FNR==1) {for(i=1;i<=max;++i) if(i in a) a[i]=++c }
     {for(i=1;i<=NF;++i) $i=a[$i]}1' file file

Если file имеет в качестве ввода:

1 2
1 3
2 5
6 9
3 5
7 9
11 6
7 11

Вышеприведенная команда вернет:

1 2
1 3
2 4
5 7
3 4
6 7
8 5
6 8

Идея этого метода заключается в отслеживании массива a, который индексируется старым значением и возвращает новое значение: a[old]=new. Мы дважды сканируем файл и сохраняем все возможные значения в a[old]. Когда мы читаем файл во второй раз, мы сначала проверяем, какими будут новые значения. Когда это будет сделано, мы просто обновим все поля новыми значениями и напечатаем результат.

Вышесказанное также можно сделать, прочитав файл один раз, вам просто нужно немного буферизовать:

awk '{b[FNR]=$0;for(i=1;i<=NF;++i) {a[$i]; max=(max<$i?$i:max)}}
     END {
        for(i=1;i<=max;++i) if(i in a) a[i]=++c
        for(n=1;n<=FNR;++n) {
          $0=b[n]
          for(i=1;i<=NF;++i) $i=a[$i]
          print
        }
     }' file
3 голосов
/ 28 февраля 2020

Используя GNU awk и asorti():

$ gawk '{                         # GNU awk only or implement sort
    a[$1];a[$2]                   # hash field values to a array
    f1[NR]=$1;f2[NR]=$2           # hash fields $1 and $2 index on NR
}
END {                             # after all data is hashed
    asorti(a,a,"@ind_num_asc")    # sort index of a where the values are
    for(i in a)                   # make a reverse map 
        b[a[i]]=i
    for(i=1;i<=NR;i++)            # iterate the stored "records"
        print b[f1[i]],b[f2[i]]   # print and fetch from reverse map
}' file

a[] сохраняет значения поля уникальности: a[6] a[5] затем asorti() переиндексирует a[]: a[1]=5 a[2]=6 и мы получаем соответствующие новые ценности. b[] - обратное отображение a[]: b[5]=1 b[6]=2, которое используется для получения новых значений для старых значений полей при выводе.

Вывод:

1 2
1 3
2 4
5 7
3 4
6 7
8 5
6 8
0 голосов
/ 28 февраля 2020

Предполагается, что ваш ввод выглядит следующим образом:

input.txt

2 1
4 3
5 5
6 2
1 4
8 7
9 6
7 9

Примечание: № 3 в столбце col1 и 8 в столбце col2 только для упрощения отслеживания.

Затем отсортируйте каждый столбец по отдельности и сохраните его:

$sort -k1,1 input.txt | awk '{ print $1}'  > 1_sorted
$cat 1_sorted
1
2
4
5
6
7
8
9


$sort -k2,2 input.txt | awk '{ print $2}'  > 2_sorted
$cat 2_sorted
1
2
3
4
5
6
7
9

Теперь просто объедините два файла:

$paste -d' ' 1_sorted 2_sorted > merged_again

$ cat merged_again
1 1
2 2
4 3
5 4
6 5
7 6
8 7
9 9

Возможно, существует более производительный / элегантный метод, но я не могу подумай об этом прямо сейчас.

...