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

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

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, что я и собираюсь.

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

5
задан 10 June 2015 в 09:34

4 ответа

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

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

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

Вывод такой:

    46)serg@ubuntu[/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 {}

0
ответ дан 10 June 2015 в 09:34

Данный 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
3
ответ дан 10 June 2015 в 09:34

Используя 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 чтения строка файла указатель увеличен одним для указания на следующую строку, мы используем это, чтобы считать и распечатать до строки, которая содержит [только 114].

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

  • Для получения точности на результирующем числе с плавающей точкой мы использовали decimal модуль.

0
ответ дан 10 June 2015 в 09:34

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

Проблема:

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

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

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

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

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

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

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

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

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


Это уменьшится 30 значение первых 3 разделенных пробелом чисел в каждой строке после первого соответствия строки 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
1
ответ дан 10 June 2015 в 09:34

Другие вопросы по тегам:

Похожие вопросы: