如何在Fortran中更好地解析变量时间戳信息?

时间:2015-02-15 00:10:49

标签: parsing optimization timestamp fortran

我正在gfortran中编写代码,将可变时间戳分成年份,月份和日期的单独部分。我已经编写了这段代码,因此用户可以输入时间戳格式(即年/月/日,日/月/年等)。这创建了总共6种可能的组合。我已经编写了试图解决这个问题的代码,但我认为它很丑陋且做得不好。

我目前的代码使用了很多' if'并且'转到'声明。用户提供时间戳格式的< tsfo' ' TS'是包含时间戳数据(多达100,000个时间戳)的字符数组。 ' tsdelim'是年,月和日之间的分隔符。我必须从' frd' (第一个时间戳)到' nlines' (最后一个时间戳)。

这是相关代码。

* Choose which case to go to. 
first = INDEX(tsfo,tsdelim)
second = INDEX(tsfo(first+1:),tsdelim) + first
if (INDEX(tsfo(1:first-1),'YYYY') .ne. 0) THEN
   if (INDEX(tsfo(first+1:second-1),'MM') .ne. 0) THEN
      goto 1001
   else
      goto 1002
   end if
else if (INDEX(tsfo(1:first-1),'MM') .ne. 0) THEN
   if (INDEX(tsfo(first+1:second-1),'DD') .ne. 0) THEN
      goto 1003
   else
      goto 1004
   end if
else if (INDEX(tsfo(1:first-1),'DD') .ne. 0) THEN
   if (INDEX(tsfo(first+1:second-1),'MM') .ne. 0) THEN
      goto 1005
   else
      goto 1006
   end if
end if

first = 0
second = 0

* Obtain the Julian Day number of each data entry. 
* Acquire the year, month, and day of the time stamp. 
* Find 'first' and 'second' and act accordingly. 

* Case 1: YYYY/MM/DD
1001    do i = frd,nlines
       first = INDEX(ts(i),tsdelim)
       second = INDEX(ts(i)(first+1:),tsdelim) + first
       read (ts(i)(1:first-1), '(i4)') Y
       read (ts(i)(first+1:second-1), '(i2)') M
       read (ts(i)(second+1:second+2), '(i2)') D
* Calculate the Julian Day number using a function. 
       temp1(i) = JLDYNUM(Y,M,D)
end do
goto 1200
* Case 2: YYYY/DD/MM
1002    do i = frd,nlines
       first = INDEX(ts(i),tsdelim)
       second = INDEX(ts(i)(first+1:),tsdelim) + first
       read (ts(i)(1:first-1), '(i4)') Y
       read (ts(i)(second+1:second+2), '(i2)') M
       read (ts(i)(first+1:second-1), '(i2)') D
* Calculate the Julian Day number using a function. 
       temp1(i) = JLDYNUM(Y,M,D)
end do
goto 1200

* Onto the next part of the code
1200 blah blah blah

我相信这段代码会有效,但我认为这不是一个很好的方法。有没有更好的方法来解决这个问题?

重要的是要注意指数' first'和第二个'必须为每个时间戳计算,因为月和日都可以用1或2个整数表示。年份始终以4表示。

2 个答案:

答案 0 :(得分:0)

我会用另一种方式编码不同的情况,如:

module foo
  implicit none
  private
  public encode_datecode
contains
  integer function encode_datecode(datestr, sep)
    character(len=*), intent(in) :: datestr, sep
    integer :: first, second
    character(len=1) :: c1, c2, c3
    first = index(datestr, sep)
    second = index(datestr(first+1:), sep) + first
    c1 = datestr(1:1)
    c2 = datestr(first+1:first+1)
    c3 = datestr(second+1:second+1)
    foo = num(c1) + 3*num(c2) + 9*num(c3)
  end function encode_datecode

  integer function num(c)
    character(len=1) :: c
    if (c == 'Y') then
        num = 0
    else if (c == 'M') then
        num = 1
    else if (c == 'D') then
        num = 2
    else
        stop "Illegal character"
    end if
  end function num
end module foo

然后在SELECT声明中处理法律案件(21,15,19,7,11,5)。

这利用了不存在'YDDY / MY / YM'格式的事实。

如果您希望更好的二进制或十进制可读性,您也可以乘以4或乘以10而不是3。

答案 1 :(得分:0)

只需处理六个排列,我只需构建一个查找表,其中以tsfo字符串为键,年,月,日(第1,第2或第3)的位置为值。任何不受支持的格式都会产生错误,我在下面没有编写。当你随后循环通过你的ts列表并拆分一个项目时,你知道要转换为年,月和日整数变量的位置:

PROGRAM timestamp
  IMPLICIT NONE

  CHARACTER(len=10) :: ts1(3) = ["2000/3/4  ","2000/25/12","2000/31/07"] 
  CHARACTER(len=10) :: ts2(3) = ["3/4/2000  ","25/12/2000","31/07/2000"] 

  CALL parse("YYYY/DD/MM",ts1)
  print*
  CALL parse("DD/MM/YYYY",ts2)

CONTAINS

  SUBROUTINE parse(tsfo,ts)
    IMPLICIT NONE
    CHARACTER(len=*),INTENT(in) :: tsfo, ts(:)

    TYPE sti
       CHARACTER(len=10) :: stamp = "1234567890"
       INTEGER :: iy = -1, im = -1, id = -1
    END TYPE sti
    TYPE(sti),PARAMETER :: stamps(6) = [sti("YYYY/MM/DD",1,2,3), sti("YYYY/DD/MM",1,3,2),&
                                        sti("MM/DD/YYYY",2,3,1), sti("DD/MM/YYYY",3,2,1),&
                                        sti("MM/YYYY/DD",2,1,3), sti("DD/YYYY/MM",3,1,2)]
    TYPE(sti) :: thisTsfo
    INTEGER :: k, k1, k2
    INTEGER :: y, m, d
    CHARACTER(len=10) :: cc(3)

    DO k=1,SIZE(stamps)
      IF(TRIM(tsfo) == stamps(k)%stamp) THEN
        thisTsfo = stamps(k)
        EXIT
      ENDIF
    ENDDO
    print*,thisTsfo

    DO k=1,SIZE(ts)
      k1 = INDEX(ts(k),"/")
      k2 = INDEX(ts(k),"/",BACK=.TRUE.)
      cc(1) = ts(k)(:k1-1)
      cc(2) = ts(k)(k1+1:k2-1)
      cc(3) = ts(k)(k2+1:)
      READ(cc(thisTsfo%iy),'(i4)') y
      READ(cc(thisTsfo%im),'(i2)') m
      READ(cc(thisTsfo%id),'(i2)') d
      PRINT*,ts(k),y,m,d
    ENDDO
  END SUBROUTINE parse
END PROGRAM timestamp