У меня есть файл, который выглядит как
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
, что я и собираюсь.
Спасибо за помощь!
Вот моя версия 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 {}
Данный 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
Используя 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
модуль.
Я не могу предоставить хорошее решение проблемы, но я попытаюсь описать подробно, что проблема, и я предоставлю частичное решение.
Проблема:
Числа с плавающей точкой на машинах страдают от ограниченной точности: короче говоря, только ограниченное подмножество чисел с плавающей точкой [на каждый порядок величины] является представимым.
Числа с плавающей точкой на машинах представлены тесно после нормализованной нотации ± 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