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

时间:2019-02-25 16:21:25

标签: fortran fortran90

我有一个很大的文本文件,用分号(;)分隔。号码 具有千位分隔符和十进制分隔符的格式 昏迷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行(单位= 123,文件='test.csv') Fortran运行时错误:列表输入第3项中的实数错误

3 个答案:

答案 0 :(得分: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字符不太确定。

答案 1 :(得分: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

答案 2 :(得分: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个字符串并将它们连接起来。