我正在将一个输入文件写入一个源于60年代的程序,它从文本文件的固定宽度数据字段读取数据。格式为:
'.'
或以指数格式写入,例如: '1.23e8'
我最接近的是
print "{0:8.3g}".format(number)
,其'1.23e+06'
与1234567
,' 1234'
与1234
相符。
我想调整一下,以获得
'1234567.'
1234567
(即不是指数格式之前)
要求),' 1234.'
1234
(即以点结尾,因此不会被解释为整数),'1.235e+7'
与12345678
(即指数只使用一位数字),'-1.23e+7'
-1234567
(即未违反最高8位数字)
负数)。由于这是(据我记得)很容易用Fortran实现的,问题可能会出现,然后在与遗留代码进行交互时,我怀疑必须有一些简单的方法来做到这一点?
答案 0 :(得分:1)
您已经非常接近,但我认为您的最终解决方案将涉及编写自定义格式化程序。例如,我不相信mini-formatting language可以控制指数的宽度。
(顺便说一句,在你的第一个例子中,你没有" +"在" e&#34之后;但在你做的其他人中。明确哪一个你希望可以帮助其他回答者。)
如果我正在编写这种格式化函数,我要做的第一件事就是为它编写一套完整的测试。 doctest或unittest都适合。
然后,您将处理格式化功能,直到所有这些测试通过。
答案 1 :(得分:0)
您可以做类似的事情,坦白地说,这有点晚了,我花了太长时间,但是当我试图找出类似的东西时我就想到了。
import unittest
class TestStringMethods(unittest.TestCase):
def test_all(self):
test = (
("1234567.", 1234567),
("-123456.", -123456),
("1.23e+13", 12345678901234),
("123.4567", 123.4567),
("123.4568", 123.45678),
("1.234568", 1.2345678),
("0.123457", 0.12345678),
(" 1234.", 1234),
("1.235e+7", 12345678),
("-1.23e+6", -1234567),
)
max_char = 8
max_number = int("9" * (max_char - 1)) # 9,999,999
min_number = -int("9" * (max_char - 2)) # -999,999
for expected, given in test:
# for small numbers
# if -999,999 < given < 9,999,999:
if min_number < given < max_number:
# output = f"{given:7}"
output = f"{given:{max_char - 1}}"
# converting ints to floats without adding zero
if '.' not in output:
output += '.'
# floats longer than 8 will need rounding to fit max length
elif len(output) > max_char:
# output = str(round(given, 7 - str(given).index(".")))
output = str(round(given, max_char - 1 - str(given).index(".")))
else:
# for exponents
# added a loop for super large numbers or negative as "-" is another char
# Added max(max_char, 5) to account for max length of less than 5, was having too much fun
for n in range(max(max_char, 5) - 5, 0, -1):
fill = f".{n}e"
output = f"{given:{fill}}".replace('+0', '+')
# if all good stop looping
if len(output) == max_char:
break
else:
raise ValueError(f"Number is too large to fit in {max_char} characters", given)
self.assertEqual(len(output), max_char, msg=output)
self.assertEqual(output, expected, msg=given)
if __name__ == '__main__':
unittest.main()
答案 2 :(得分:0)
我只是通过@ Harvey251回答了问题,但分为测试部分和生产中需要的部分。
用法是:
# save the code at the end as formatfloat.py and then
import formatfloat
# do this first
width = 8
ff8 = formatfloat.FormatFloat(width)
# now use ff8 whenever you need
print(ff8(12345678901234))
这是解决方案。将代码另存为formatfloat.py并导入以使用FlotFormat类。就像我在下面说的那样,计算的循环部分最好移到FormatFlot类的init部分。
import unittest
class FormatFloat:
def __init__(self, width = 8):
self.width = width
self.maxnum = int('9'*(width - 1)) # 9999999
self.minnum = -int('9'*(width - 2)) # -999999
def __call__(self, x):
# for small numbers
# if -999,999 < given < 9,999,999:
if x > self.minnum and x < self.maxnum:
# o = f'{x:7}'
o = f'{x:{self.width - 1}}'
# converting int to float without adding zero
if '.' not in o:
o += '.'
# float longer than 8 will need rounding to fit width
elif len(o) > self.width:
# output = str(round(x, 7 - str(x).index(".")))
o = str(round(x, self.width-1 - str(x).index('.')))
else:
# for exponents
# added a loop for super large numbers or negative as "-" is another char
# Added max(max_char, 5) to account for max length of less
# than 5, was having too much fun
# TODO can i come up with a threshold value for these up front,
# so that i dont have to do this calc for every value??
for n in range(max(self.width, 5) - 5, 0, -1):
fill = f'.{n}e'
o = f'{x:{fill}}'.replace('+0', '+')
# if all good stop looping
if len(o) == self.width:
break
else:
raise ValueError(f"Number is too large to fit in {self.width} characters", x)
return o
class TestFormatFloat(unittest.TestCase):
def test_all(self):
test = (
("1234567.", 1234567),
("-123456.", -123456),
("1.23e+13", 12345678901234),
("123.4567", 123.4567),
("123.4568", 123.45678),
("1.234568", 1.2345678),
("0.123457", 0.12345678),
(" 1234.", 1234),
("1.235e+7", 12345678),
("-1.23e+6", -1234567),
)
width = 8
ff8 = FormatFloat(width)
for expected, given in test:
output = ff8(given)
self.assertEqual(len(output), width, msg=output)
self.assertEqual(output, expected, msg=given)
if __name__ == '__main__':
unittest.main()
答案 3 :(得分:0)
我对yosukesabai所做的贡献做了一点补充,以解决这种罕见的情况:四舍五入将使字符串的宽度为7个字符而不是8个字符!
class FormatFloat:
def __init__(self, width = 8):
self.width = width
self.maxnum = int('9'*(width - 1)) # 9999999
self.minnum = -int('9'*(width - 2)) # -999999
def __call__(self, x):
# for small numbers
# if -999,999 < given < 9,999,999:
if x > self.minnum and x < self.maxnum:
# o = f'{x:7}'
o = f'{x:{self.width - 1}}'
# converting int to float without adding zero
if '.' not in o:
o += '.'
# float longer than 8 will need rounding to fit width
elif len(o) > self.width:
# output = str(round(x, 7 - str(x).index(".")))
o = str(round(x, self.width - 1 - str(x).index('.')))
if len(o) < self.width:
o+=(self.width-len(o))*'0'
else:
# for exponents
# added a loop for super large numbers or negative as "-" is another char
# Added max(max_char, 5) to account for max length of less
# than 5, was having too much fun
# TODO can i come up with a threshold value for these up front,
# so that i dont have to do this calc for every value??
for n in range(max(self.width, 5) - 5, 0, -1):
fill = f'.{n}e'
o = f'{x:{fill}}'.replace('+0', '+')
# if all good stop looping
if len(o) == self.width:
break
else:
raise ValueError(f"Number is too large to fit in {self.width} characters", x)
return o