Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/283.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Python中将浮点数格式化为最大固定宽度_Python_String_Numbers_Number Formatting - Fatal编程技术网

如何在Python中将浮点数格式化为最大固定宽度

如何在Python中将浮点数格式化为最大固定宽度,python,string,numbers,number-formatting,Python,String,Numbers,Number Formatting,我正在将一个输入文件写入一个根在60年代的程序,它从文本文件的固定宽度数据字段读取数据。格式为: 字段宽度8个字符 浮点数必须包含。或以指数格式写入,例如'1.23e8' 我得到的最接近的是 print "{0:8.3g}".format(number) 这将产生'1.23e+06'和1234567,以及'1234'和1234 我想调整这个,但是,得到 '1234567.带有1234567(即,在使用之前不使用指数格式 必需) '1234.带有1234(即以点结尾,因此不解释为整数) '

我正在将一个输入文件写入一个根在60年代的程序,它从文本文件的固定宽度数据字段读取数据。格式为:

  • 字段宽度8个字符
  • 浮点数必须包含
    或以指数格式写入,例如
    '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
    (即不违反 负数)

因为这(据我回忆)很容易用Fortran实现,而且在与遗留代码交互时,问题可能会时不时地出现,所以我怀疑一定有一些简单的方法可以做到这一点?

您显然已经非常接近了,但我认为您的最终解决方案将涉及编写自定义格式化程序。例如,我不相信这个函数可以像你想要的那样控制指数的宽度

(顺便说一句,在你的第一个例子中,在“e”后面没有“+”,但在其他例子中你有。弄清楚你想要哪一个可能会帮助其他回答者。)

如果我正在编写这个格式化函数,我要做的第一件事就是为它编写一套完整的测试。两者中的一个或两个都合适


然后你就开始处理格式化函数,直到所有这些测试都通过。

你可以做一些事情,比如说,承认有点晚了,我花了太多时间在它上面,但我在尝试找出类似的东西时发现了这一点

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()
导入单元测试
类TestStringMethods(unittest.TestCase):
def测试全部(自):
测试=(
("1234567.", 1234567),
("-123456.", -123456),
(“1.23e+13”,1234567890134),
("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),
)
最大字符数=8
max_number=int(“9”*(max_char-1))#999999
最小字符数=-int(“9”*(最大字符数-2))#-999999
对于预期,在测试中给出:
#对于小数字
#如果-999999<给定值<9999999:
如果最小值<给定值<最大值:
#输出=f“{给定:7}”
输出=f“{给定:{max_char-1}”
#将整数转换为浮点而不加零
如果输出中没有“.”:
输出+='。'
#大于8的浮动需要舍入以适应最大长度
elif len(输出)>最大字符数:
#输出=str(四舍五入(给定,7-str(给定)。索引(“.”))
输出=str(四舍五入(给定,最大字符数-1-str(给定)。索引(“.”))
其他:
#代表
#为超大数字或负数添加了一个循环,因为“-”是另一个字符
#添加了max(max_char,5)来解释小于5的最大长度,这太有趣了
对于范围内的n(最大值(最大字符数,5)-5,0,-1):
fill=f“{n}e”
输出=f“{给定:{fill}}”。替换('+0','+'))
#如果一切顺利,停止循环
如果len(输出)=max_char:
打破
其他:
raise VALUERROR(f“数字太大,无法容纳{max_char}个字符”,给定)
self.assertEqual(len(输出),max_char,msg=output)
self.assertEqual(输出,预期,消息=给定)
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
unittest.main()

我只接受了@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()
导入单元测试
类格式浮点:
定义初始值(自,宽度=8):
self.width=宽度
self.maxnum=int('9'*(宽度-1))#999999
self.minnum=-int('9'*(宽度-2))#-99999
定义调用(self,x):
#对于小数字
#如果-999999<给定值<9999999:
如果x>self.minnum和x自身宽度:
#输出=str(圆形(x,7-str(x).索引(“.”))
o=str(圆形(x,self.width-1-str(x).index('.'))
其他:
#代表
#为超大数字或负数添加了一个循环,因为“-”是另一个字符
#添加了max(max_char,5)以说明小于的最大长度
#5岁以上,玩得太开心了
#TODO我能提前为这些设定一个阈值吗,
#所以我不必对每个值都进行计算??
对于范围内的n(最大(自宽,5)-5,0,-1):
fill=f'{n}e'
o=f'{x:{fill}}'。替换('+0','+'))
#如果一切顺利,停止循环
如果len(o)=自宽:
打破
其他:
提升值错误(f“编号太小
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