Конкретные изменения в текстовом файле (awk?)

5

У меня есть файл, который выглядит как

TITLE
1.000000000000000
10.0000000000000000    0.0000000000000000    0.0000000000000000 
0.0000000000000000   10.0000000000000000    0.0000000000000000
0.0000000000000000    0.0000000000000000   10.0000000000000000
U   U
X   X
C
0.2000000000000028  0.2000000000000028  0.2000000000000028
0.2967599999999990  0.0641000000000034  0.1551499999999990
0.1033699999999982  0.3361099999999979  0.244990000000001

, и мне нужно иметь скрипт, который будет изменять нижний числовой блок (ниже C до 30 меньше, чем их исходные значения). Есть ли такой способ сделать это?

Пока лучшее, что у меня есть, это

$ awk '{if(NR>1){for(i=2;i<=NF;i++){$(i)=$(i)-10;}}print;}' data.txt | column -t

но, это из Интернета, и я не уверен, как самому манипулировать им с желаемым эффектом. Это, однако, не будет печатать / перезаписывать то, что в настоящее время находится в data.txt , для чего я и собираюсь.

Спасибо за помощь!

    
задан MadisonCooper 09.06.2015 в 22:36
источник

4 ответа

1

Вот моя версия awk:

awk '/^C/,0 {for (i=1;i<=NF;i++) { if ( $i != "C" ) printf "%.16f ",$i-30.0000};print"\n" }' data.txt

Здесь мы получаем весь материал от символа C до конца файла, вычитаем 30 в каждом столбце, добавляем новую строку и повторяем процесс. Оператор if добавлен, чтобы избежать вычитания 30 из C. Очевидно,

Вывод:

    46)[email protected][/home/xieerqi]
    >_ awk '/^C/,0 {for (i=1;i<=NF;i++) { if ( $i != "C" )  printf "%.16f ",$i-30.0000};print"\n" }' data.txt                             

-29.7999999999999972 -29.7999999999999972 -29.7999999999999972 

-29.7032400000000010 -29.9358999999999966 -29.8448500000000010 

-29.8966300000000018 -29.6638900000000021 -29.7550099999999986 

Это может быть заменено в исходном файле; Альтернативно мы всегда могли попробовать распечатать материал до C с помощью инструкции BEGIN {}

    
ответ дан Sergiy Kolodyazhnyy 09.06.2015 в 23:23
источник
3

Указанный data.awk ниже:

{
        if (matched) {
                for (i = 1; i <= NF; i++) {
                        $(i) = 30.0 - $(i)
                }
        }
        print
}
/^C/ { matched = 1 }
BEGIN { CONVFMT = "%.20f" }

Вы получаете:

$ awk -f data.awk data.txt
TITLE
1.000000000000000
10.0000000000000000    0.0000000000000000    0.0000000000000000 
0.0000000000000000   10.0000000000000000    0.0000000000000000
0.0000000000000000    0.0000000000000000   10.0000000000000000
U   U
X   X
C
29.79999999999999715783 29.79999999999999715783 29.79999999999999715783
29.70324000000000097543 29.93589999999999662350 29.84485000000000098908
29.89663000000000181444 29.66389000000000208956 29.75500999999999862666

У вас, очевидно, проблема с точностью для ввода. Таким образом, вы можете вызвать команду bc для фактических вычислений (она поддерживает произвольную точность):

{
        if (matched) {
                for (i = 1; i <= NF; i++) {
                        cmd = "echo 30.0 - " $(i) " | bc"
                        cmd | getline $(i)
                        close(cmd)
                }
        }
        print
}
/^C/ { matched = 1 }

Результат:

TITLE
1.000000000000000
10.0000000000000000    0.0000000000000000    0.0000000000000000 
0.0000000000000000   10.0000000000000000    0.0000000000000000
0.0000000000000000    0.0000000000000000   10.0000000000000000
U   U
X   X
C
29.7999999999999972 29.7999999999999972 29.7999999999999972
29.7032400000000010 29.9358999999999966 29.8448500000000010
29.8966300000000018 29.6638900000000021 29.755009999999999

Чтобы перезаписать data.txt с результатом, вам обычно нужно записать его в другой файл, а затем переименуйте его в исходный файл.

$ awk -f data.awk data.txt > data.txt.out
$ mv data.txt.out data.txt

Или используйте sponge в moreutils .

$ sudo apt-get install moreutils
$ awk -f data.awk data.txt | sponge data.txt
    
ответ дан yaegashi 09.06.2015 в 23:08
1

Использование python :

#!/usr/bin/env python2
import decimal
with open('/path/to/data.txt') as f:
    for line in f:
        if line.rstrip() == 'C':
            print line.rstrip()
            break
        else:
            print line.rstrip()
    for line in f:
        print '\t'.join(['{0:.16f}'.format(decimal.Decimal(30 - float(part))) for part in line.rstrip().split()])

Выход:

TITLE
1.000000000000000
10.0000000000000000    0.0000000000000000    0.0000000000000000
0.0000000000000000   10.0000000000000000    0.0000000000000000
0.0000000000000000    0.0000000000000000   10.0000000000000000
U   U
X   X
C
29.7999999999999972 29.7999999999999972 29.7999999999999972
29.7032400000000010 29.9358999999999966 29.8448500000000010
29.8966300000000018 29.6638900000000021 29.7550099999999986
  • Каждый раз, когда python читает строку файла, указатель увеличивается на единицу, указывая на следующую строку, мы используем это для чтения и печати до строки, содержащей только C .

  • Для строк после C мы разделили строку на части на line.rstrip().split() , а затем вычитали каждую часть из 30 , чтобы получить желаемый результат.

  • Чтобы получить точность для полученного числа с плавающей запятой, мы использовали модуль decimal .

ответ дан heemayl 09.06.2015 в 22:56
1

Я не могу обеспечить хорошее решение проблемы, но я попытаюсь подробно описать, в чем проблема, и я предоставлю частичное решение.

Проблема :

Числа с плавающей запятой на машинах имеют ограниченную точность: короче говоря, только ограниченное подмножество чисел с плавающей запятой [на каждый порядок величины] представляется.

Числа с плавающей запятой на машинах представлены близко по нормированной нотации ± significand * base ^ exponent (где base = база представления, significand = любое действительное число > 0 и < = база представления и где exponent = порядок величины): например, на 32-битной машине, следующей за стандартом IEEE 754 , числа с плавающей запятой с одинарной точностью представлены с использованием первого бита для представления знака, следующие 8 бит для представления порядка величины и последние 23 бита для представления значения, в то время как числа с плавающей запятой с двойной точностью представлены с использованием первого бита для представления знака, следующие 11 бит для представления порядка величины и последние 52 бита для представления значащего (базового , будучи всегда 2, не представляется). Для этого значимое значение числа должно быть всегда представлено 23 битами (с использованием одной точности) или с использованием 52 бит (с использованием двойной точности).

Свойство этого способа представления чисел с плавающей запятой в фиксированном числе битов состоит в том, что если число представляемых значений на порядок величины всегда одинаково, среднее «расстояние» между представляемыми числами с плавающей запятой с тем же порядком величина возрастает по мере увеличения порядка двух величин.

Для вышеизложенного первая проблема заключается в том, что если значение значащего значения нормированной нотации с плавающей запятой не находится в ограниченном наборе представимых значений, оно округляется до ближайшего (более высокого или нижнего) значения и представляемого.

Говоря о числах, представленных с одинаковым порядком величины, вторая проблема заключается в том, что даже когда число с плавающей запятой представляется точно, добавление / вычитание другого [точно представляемого] числа с плавающей запятой в него может привести к не точно представимому плаванию номер точки, значение которого будет округлено до ближайшего (более высокого или нижнего) значения, представляемого.

Наконец, говоря о числах, представленных с другим порядком величины, третья проблема (в основном из-за архитектуры ЦП) заключается в том, что для того, чтобы иметь возможность выполнять дополнения / подстановки между числами с плавающей запятой, представленными с другим порядком величины , числа должны быть сначала представлены с использованием того же порядка величины; это означает, что порядок наименьшего порядка необходимо увеличить, и что (чтобы сбалансировать это) его значение необходимо сдвинуть вправо с последующей потерей количества бит, превышающего 23/252; если этого недостаточно, числа с плавающей запятой со значительной разницей по порядку величины могут привести к тому, что после добавления / вычитания точно в количестве с самым высоким абсолютным значением это для уже заявленной проблемы (недостаточно разницы, представляемое значение вверх / вниз до другого более высокого / нижнего представляемого значения) и все хуже, поскольку порядок величины двух чисел расходится далее.

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

Частичное решение :

Для вышеизложенного результаты этих awk однострочных неточны; это могло быть смягчено за счет использования двойной точности в своих командах printf , но это не поддерживается.

Это уменьшит на 30 значение первых трех разделенных пробелами чисел в каждой строке после первой строки, соответствующей C , сохраняя формат чисел. Поскольку версия awk , включенная в Ubuntu, не поддерживает исправления на месте, вам придется либо использовать awk , либо перенаправить свой stdout в файл с помощью bash > оператора или использовать gawk (GNU awk ) > = 4.10.0 ;

Использование awk :

awk 'NR==1, $0=="C"; $0=="C", 0 {if ($0!="C") printf "%.16f  %.16f  %.16f\n", $1-30, $2-30, $3-30}' data.txt > data_processed.txt

Использование gawk (GNU awk ) > = 4.10.0

gawk -i inplace 'NR==1, $0=="C"; $0=="C", 0 {if ($0!="C") printf "%.16f  %.16f  %.16f\n", $1-30, $2-30, $3-30}' data.txt
  • NR==1, $0=="C"; : выбор и печать всех записей между первым и первым соответствием C включительно;
  • $0=="C", 0 {if ($0!="C") printf "%.16f %.16f %.16f\n", $1-30, $2-30, $3-30} : выбирает все записи между первым сопоставлением C и последним включительным и печатает 1-е, 2-е и 3-е поля каждой выбранной записи, не совпадающие с C двойного пробела, разделенные и уменьшенные на 30 , сохраняя исходный формат номера;

Пример вывода:

~/tmp$ cat data.txt
TITLE
1.000000000000000
10.0000000000000000    0.0000000000000000    0.0000000000000000 
0.0000000000000000   10.0000000000000000    0.0000000000000000
0.0000000000000000    0.0000000000000000   10.0000000000000000
U   U
X   X
C
0.2000000000000028  0.2000000000000028  0.2000000000000028
0.2967599999999990  0.0641000000000034  0.1551499999999990
0.1033699999999982  0.3361099999999979  0.244990000000001
~/tmp$ awk 'NR==1, $0=="C"; $0=="C", 0 {if ($0!="C") printf "%.16f  %.16f  %.16f\n", $1-30, $2-30, $3-30}' data.txt
TITLE
1.000000000000000
10.0000000000000000    0.0000000000000000    0.0000000000000000 
0.0000000000000000   10.0000000000000000    0.0000000000000000
0.0000000000000000    0.0000000000000000   10.0000000000000000
U   U
X   X
C
-29.7999999999999972  -29.7999999999999972  -29.7999999999999972
-29.7032400000000010  -29.9358999999999966  -29.8448500000000010
-29.8966300000000018  -29.6638900000000021  -29.7550099999999986
    
ответ дан kos 09.06.2015 в 23:48