我有一个很大的文本文件,用分号(;)分隔。号码 具有千位分隔符和十进制分隔符的格式 昏迷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项中的实数错误
答案 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个字符串并将它们连接起来。