如何用千分隔点和小数分隔符逗号将数字读入fortran,例如123.456.891.234,56

问题描述 投票:1回答:3

我有一个巨大的文本文件,用分号(;)分隔。数字具有千分隔点和小数分隔符昏迷的格式,例如123.456.891.234,56

我不想用编辑器搜索和替换文件中的点,因为我没有权限更改文件。

我可以把它读成字符串并试图摆脱这些点。但它似乎不是解决问题的好方法。

program prjRead
  implicit none

  integer:: a
  real(8) :: b
  real(8) :: c
  character(10) :: dummy

  open (123,file = "test.csv", DECIMAL='COMMA')

  read(123,*) dummy
  read(123,*) a, &
              b, &
              c

  write(*,*) a,b,c

end program prjRead

test.csv的内容

integer;decimalcomma;thousandsep
5;56,67;123.456,78

在文件prjRead.f90的第36行(unit = 123,file ='test.csv')Fortran运行时错误:列表输入的第3项中的实数错误

fortran fortran90
3个回答
1
投票

如果你真的想在Fortran中这样做,那就不难了。首先,让我们有一个函数来从字符串中删除所有出现的.

  FUNCTION drop_stops(instr) RESULT(outstr)
    CHARACTER(len=*), INTENT(in) :: instr
    CHARACTER(len=:), ALLOCATABLE :: outstr
    CHARACTER(len=1), DIMENSION(:), ALLOCATABLE :: str_array

    ALLOCATE(str_array(LEN_TRIM(instr)))
    str_array = TRANSFER(instr,str_array)
    str_array = PACK(str_array,str_array /= '.')
    ALLOCATE(CHARACTER(len=SIZE(str_array))::outstr)
    outstr = TRANSFER(str_array,outstr)
  END FUNCTION drop_stops

我相信这很明显,除了您不熟悉的任何函数或语句的文档之外不需要任何解释。

然后,坚持你的原始代码并声明另一个字符串变量你可以读取dummy,就像你已经做的那样,然后写一些像

 dummy_without_stops = drop_stops(dummy)

现在你可以对它进行内部阅读以获得你感兴趣的数字,例如

read(dummy_without_stops,*,decimal='comma') a, b, c

请注意,drop_stops中实现的方法依赖于字符串中的字符顺序排列在内存中,并匹配数组中字符的相同存储。我相信这适用于ASCII字符,不太确定ISO_10646字符。


0
投票

您没有从输入文件中提供足够大的样本。但实质上,您需要先使用分隔符;拆分文件内容。然后,对于您获得的每个字符串编号,将所有千位分隔符.替换为“”(无)。然后,用正常的十进制表示法,替换小数符号.。这是尝试通过下面的splitStr()replaceStr()类型绑定程序实现这一点,在您提供的示例文件内容行上测试5;56,67;123.456,78

module String_mod

    use, intrinsic :: iso_fortran_env, only: IK=>int32, RK=>real64
    implicit none

    public

    character(*), parameter :: MODULE_NAME = "@String_mod"

    type :: CharVec_type
        character (:), allocatable  :: record
    end type CharVec_type

    type :: String_type
        character(:)      , allocatable   :: value
        type(CharVec_type), allocatable   :: Parts(:)
        integer(IK)                       :: nPart = 0
    contains
        procedure, nopass :: replaceStr, splitStr, str2num
    end type String_type

!***********************************************************************************************************************************
!***********************************************************************************************************************************

contains

!***********************************************************************************************************************************
!***********************************************************************************************************************************

    recursive function replaceStr(string,search,substitute) result(modifiedString)
        implicit none
        character(len=*), intent(in)  :: string, search, substitute
        character(len=:), allocatable :: modifiedString
        integer(IK)                   :: i, stringLen, searchLen
        stringLen = len(string)
        searchLen = len(search)
        if (stringLen==0 .or. searchLen==0) then
            modifiedString = ""
            return
        elseif (stringLen<searchLen) then
            modifiedString = string
            return
        end if
        i = 1
        do
            if (string(i:i+searchLen-1)==search) then
                modifiedString = string(1:i-1) // substitute // replaceStr(string(i+searchLen:stringLen),search,substitute)
                exit
            end if
            if (i+searchLen>stringLen) then
                modifiedString = string
                exit
            end if
            i = i + 1
            cycle
        end do
    end function replaceStr

!***********************************************************************************************************************************
!***********************************************************************************************************************************

    function splitStr(string,delimiter)

        implicit none
        character(len=*)  , intent(in)  :: string,delimiter
        character(len=:)  , allocatable :: dummyStr
        type(CharVec_type), allocatable :: splitStr(:)
        integer(IK)                     :: maxNumSplit
        integer(IK)                     :: stringLen, delimLen, splitCounter, currentPos

        dummyStr  = string
        delimLen  = len(delimiter)
        stringLen = len(dummyStr)

        if (delimLen==0) then
            allocate(splitStr(1))
            splitStr(1)%record = string
            return
        end if

        maxNumSplit = 1 + stringLen / delimLen
        allocate(splitStr(maxNumSplit))
        splitCounter = 1
        loopParseString: do
            if (stringLen<delimLen) then
                splitStr(splitCounter)%record = dummyStr
                exit loopParseString
            elseif (stringLen==delimLen) then
                if (dummyStr==delimiter) then
                    splitStr(splitCounter)%record = ""
                end if
                exit loopParseString
            elseif (dummyStr(1:delimLen)==delimiter) then
                dummyStr = dummyStr(delimLen+1:stringLen)
                stringLen = len(dummyStr)
                cycle loopParseString
            else
                currentPos = 2
                loopSearchString: do
                    if (dummyStr(currentPos:currentPos+delimLen-1)==delimiter) then
                        splitStr(splitCounter)%record = dummyStr(1:currentPos-1)
                        if (currentPos+delimLen>stringLen) then
                            exit loopParseString
                        else
                            splitCounter = splitCounter + 1
                            dummyStr = dummyStr(currentPos+delimLen:stringLen)
                            stringLen = len(dummyStr)
                            cycle loopParseString
                        end if
                    else
                        currentPos = currentPos + 1
                        if (stringLen<currentPos+delimLen-1) then
                            splitStr(splitCounter)%record = dummyStr
                            exit loopParseString
                        end if
                        cycle loopSearchString
                    end if
                end do loopSearchString
            end if
        end do loopParseString
        splitStr = splitStr(1:splitCounter)

    end function splitStr

!***********************************************************************************************************************************
!***********************************************************************************************************************************

    pure elemental function str2num(str)
        implicit none
        character(len=*), intent(in) :: str
        real(RK)                  :: str2num
        read(str,*) str2num
    end function str2num

!***********************************************************************************************************************************
!***********************************************************************************************************************************

end module String_mod

program readFile_prog
    use String_mod, only: String_type
    implicit none
    ! Rules: comma means decimal point. Dot means thousands separator. delimiter is ;.
    character(*), parameter :: FileToRead = "5;56,67;123.456,78"
    type(String_type)       :: String
    integer                 :: i

    ! read file
    String%value = FileToRead

    ! split file contents into individual numbers 
    String%Parts = String%splitStr(String%value,";")

    ! count the number of integers in the file
    String%nPart = size(String%Parts)

    do i = 1, String%nPart

        ! For each number, remove the thousands separator, by replacing "." with ""
        String%Parts(i)%record = String%replaceStr(String%Parts(i)%record,".","")

        ! now replace comma decimal symbols with regular . decimal notation
        String%Parts(i)%record = String%replaceStr(String%Parts(i)%record,",",".")

        ! Covert the integer number from character type to integer and print it on screen
        write(*,"(*(g0,:,' '))") "Number(",i,") = ", String%str2num(String%Parts(i)%record)

    end do

end program readFile_prog

这是输出:

$gfortran -std=f2008 *.f95 -o main
$main
Number( 1 ) =  5.0000000000000000
Number( 2 ) =  56.670000000000002
Number( 3 ) =  123456.78000000000

0
投票
program prjRead
  implicit none

  integer:: a
  real(8) :: b
  real(8) :: c
  character(18) :: header
  character(18) :: cAsString
  character(18) :: cWithOutStops

  open (123,file = "test.csv", DECIMAL='COMMA')

  read(123,*) header
  read(123,*) a, &
              b, &
              cAsString

  cWithOutStops = drop_stops(cAsString)

  read(cWithOutStops,*,decimal='comma')  c

  write(*,*) "gives c without decimal places"
  write(*,*) a,b,c

  write(*,*) "********************"
  write(*,*) "second try, the function drop_stops does what it 
promisses. If have to feed it with the right string"
  cAsString ="123.456,78"
  cWithOutStops = drop_stops(cAsString)
  read(cWithOutStops,*,decimal='comma')  c

  write(*,*) a,b,c

contains

FUNCTION drop_stops(instr) RESULT(outstr)
    CHARACTER(len=*), INTENT(in) :: instr
    CHARACTER(len=:), ALLOCATABLE :: outstr
    CHARACTER(len=1), DIMENSION(:), ALLOCATABLE :: str_array

    ALLOCATE(str_array(LEN_TRIM(instr)))
    str_array = TRANSFER(instr,str_array)
    str_array = PACK(str_array,str_array /= '.')
    ALLOCATE(CHARACTER(len=SIZE(str_array))::outstr)
    outstr = TRANSFER(str_array,outstr)
  END FUNCTION drop_stops
end program prjRead

产量

给出没有小数位的c 5 56.670000000000002 123456.00000000000 ********************第二次尝试,函数drop_stops执行它所承诺的内容。如果必须用正确的字符串5 56.670000000000002 123456.78000000000喂它

HighPerformanceMarks解决方案几乎就在那里。我只缺乏在阅读时吞下数字c的技能。或者我读了2个字符串并将它们连接起来。

© www.soinside.com 2019 - 2024. All rights reserved.