基于分隔符解析字符串的最佳方法?

时间:2016-12-02 16:30:31

标签: arrays string file parsing fortran

如果我们有一个a="2016:03:30:00:00,2,5,10,,,,,"形式的字符串,那么指定和提取 n 元素的最佳方法是什么,由* n-1 * th逗号分隔?例如。第二个元素是在第一个逗号之后。

现在我将数据从一个巨大的CSV文件(以逗号分隔,一行一行)提取到一个字符串数组中。数组的每一行都将具有上面的形式(在第n列中可能有也可能没有值),并且每行具有相同数量的分隔符。

我正在尝试仅处理特定列,以便我可以对它们进行平均等,但无法找到隔离 n 列的方法。当有问题的角色出现多次时,SCANINDEX似乎没有帮助。

或者,有没有办法读取文件并仅将 n 列分配给我的字符串数组?如果不将整个CSV行放入一个数组元素中,我无法找到方法,所以我现在希望解析每一行中的字符串将是下一个最好的事情。但是,如果我可以将其读入数组,解析为多列开始,那将是理想的。

除此之外:Fortran对于这类任务是错误的语言吗?如果需要,我可以在C中备份并执行此操作,甚至可以使用丑陋的bash脚本,但是尝试与其他Fortran应用程序并行运行。

2 个答案:

答案 0 :(得分:1)

OP写我们有一个形式为a =“2016:03:30:00:00,2,5,10 ,,,,,”的字符串所以让我们一起去,程序已经从文件中读取一行名为a的字符变量。似乎该行以日期/时间开始,然后具有固定数量的整数元素,其中一些可能不存在。给出诸如

之类的声明
character(len=128) :: elements

我们可以通过执行

来简单地删除a的前17个字符(即日期和第一个逗号)
elements = a(18:)

elements的内容分配给字符变量a并删除日期。所以在前面的语句elements看起来像

之后
"2,5,10,,,,,"

我们现在可以使用Fortran的列表定向输入来读取elements中的7个整数,并使用诸如

之类的语句
read(elements,*) nums(1:7)

现在人们可以用nums做什么,例如只保留第4个元素并回收其他元素。

这不是一个完整的答案,但我希望它足以让OP弄明白其余部分。如果没有,请澄清问题。

答案 1 :(得分:0)

以下代码与HighPerformanceMark的答案基本相同(即,使用以逗号分隔的值的列表导向输入),但是当一行以逗号结尾时(例如,行,我遇到了麻烦(=文件结束))下面的3和4)。所以,我手动为每一行添加了一个逗号来处理这种情况:

program main
    implicit none
    integer, parameter :: nrow = 4, ncol = 9
    character(100) :: csvinp( nrow ), time
    integer :: dat( nrow, ncol ), irow, icol

    csvinp( 1 ) = "2016:03:30:00:00,2,5,10,1,2,34,5,3,2"
    csvinp( 2 ) = "2017:03:40:00:00,1,2,,4,,,,,9"
    csvinp( 3 ) = "2018:03:50:00:00,,2,3,,,,7,,"
    csvinp( 4 ) = "2019:03:60:00:00,,,,,,,,,"

    do irow = 1, nrow
        csvinp( irow ) = trim(csvinp( irow )) // ","   !! add one more comma
    enddo

    dat(:,:) = 0        !! (#)
    do irow = 1, nrow
        read( csvinp( irow ), * ) time, dat( irow, : )

        print *, "irow:", irow
        print *, "  time    = ", trim( time )
        print *, "  columns = ", dat( irow, : )
    enddo

    print *
    print *, "average of each column:"
    do icol = 1, ncol
        print *, "icol=", icol, "ave=", sum( dat( :, icol ) ) / real(nrow)
    enddo
end

结果:

 irow: 1
   time    = 2016:03:30:00:00
   columns =  2 5 10 1 2 34 5 3 2
 irow: 2
   time    = 2017:03:40:00:00
   columns =  1 2 0 4 0 0 0 0 9
 irow: 3
   time    = 2018:03:50:00:00
   columns =  0 2 3 0 0 0 7 0 0
 irow: 4
   time    = 2019:03:60:00:00
   columns =  0 0 0 0 0 0 0 0 0

 average of each column:
 icol= 1 ave= 0.75
 icol= 2 ave= 2.25
 icol= 3 ave= 3.25
 icol= 4 ave= 1.25
 icol= 5 ave= 0.5
 icol= 6 ave= 8.5
 icol= 7 ave= 3.0
 icol= 8 ave= 0.75
 icol= 9 ave= 2.75

这里,最初用dat填充一些所需的值(例如,0)似乎更好,因为如果字符串有空白列,则dat的相应元素不会被修改。例如,如果我们将上面代码中的行(#)更改为dat = -100,我们就会得到

 irow: 1
   time    = 2016:03:30:00:00
   columns =  2 5 10 1 2 34 5 3 2
 irow: 2
   time    = 2017:03:40:00:00
   columns =  1 2 -100 4 -100 -100 -100 -100 9
 irow: 3
   time    = 2018:03:50:00:00
   columns =  -100 2 3 -100 -100 -100 7 -100 -100
 irow: 4
   time    = 2019:03:60:00:00
   columns =  -100 -100 -100 -100 -100 -100 -100 -100 -100

 average of each column:
 icol= 1 ave= -49.25
 icol= 2 ave= -22.75
 icol= 3 ave= -46.75
 icol= 4 ave= -48.75
 icol= 5 ave= -74.5
 icol= 6 ave= -66.5
 icol= 7 ave= -47.0
 icol= 8 ave= -74.25
 icol= 9 ave= -47.25

虽然我不确定这种行为是否符合标准,但gfortran-6,ifort-16和Oracle fortran 12.5之间的行为相同。 (实际上,我希望编译器在有空白列时填0,但事实并非如此。)