如何使用千位分隔符点和小数分隔符逗号将数字读入 Fortran,例如123.456.891.234,56
How to read a number into fortran with thousand-separator point and decimal-separator comma e.g. 123.456.891.234,56
我有一个用分号 (;) 分隔的巨大文本文件。数字
具有千位分隔符点和小数点分隔符的格式
昏迷,例如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 中的实数错误
如果你真的想在 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 个字符不太确定。
您没有从输入文件中提供足够大的样本。但本质上,您需要先通过分隔符 ;
拆分文件内容。然后,对于您获得的每个字符串编号,将所有千位分隔符 .
替换为“”(无)。然后,将小数符号,
替换为正常的十进制符号.
。这是通过下面的 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
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
Output
gives c without decimal places
5 56.670000000000002 123456.00000000000 ******************** second try, the function drop_stops does what
it promisses. If have to feed it with the right string
5 56.670000000000002 123456.78000000000
HighPerformanceMarks 解决方案即将推出。我只是缺乏在阅读时将数字c一口吞下的技巧。或者我读取 2 个字符串并将它们连接起来。
我有一个用分号 (;) 分隔的巨大文本文件。数字 具有千位分隔符点和小数点分隔符的格式 昏迷,例如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 中的实数错误
如果你真的想在 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 个字符不太确定。
您没有从输入文件中提供足够大的样本。但本质上,您需要先通过分隔符 ;
拆分文件内容。然后,对于您获得的每个字符串编号,将所有千位分隔符 .
替换为“”(无)。然后,将小数符号,
替换为正常的十进制符号.
。这是通过下面的 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
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
Output
gives c without decimal places 5 56.670000000000002 123456.00000000000 ******************** second try, the function drop_stops does what it promisses. If have to feed it with the right string 5 56.670000000000002 123456.78000000000
HighPerformanceMarks 解决方案即将推出。我只是缺乏在阅读时将数字c一口吞下的技巧。或者我读取 2 个字符串并将它们连接起来。