回 帖 发 新 帖 刷新版面

主题:如何确定文件中数据的列数?

大家好,
我从文件中读取观测数据,打文件中的观测数据的列数是不固定的(两列、三列、四列等等)。
通过什么方法可以确定该文件中数据的列数呢? 确定后就可以固定数组的维数了,也就容易计算了。

大家有什么好方法么?

回复列表 (共6个回复)

沙发

关键是找出列分隔的特殊字符(通常是 white space),然后读取一行作字符判断分析。或者如果是 Linux 系统,考虑是否可以借助外部命令行获得。

板凳

!此程序是将数据文件(要求每一行的数据个数相同,且每行首的分隔不能为逗号
!分隔符逗号和空格不能连续混合使用,空格可以连续),数据文件是整数。
!原理是:用动态分配数组的大小使用,先获取行数,再获取列数。
!      Fortran2008 by 2011-11-09
implicit none
integer count1,count2,len,m,n,error
integer, allocatable :: a(:,:)
character(len=100) buffer
open(10,file="in.txt")
open(20,file="out.txt")
!**************************************************************************
!获取数组a(:,:)的行数
!**************************************************************************
count1=0
do while(1)
    read(10,*,iostat=error)  
    if(error/=0) exit        !读数据文件的每一行,如果读到文件结尾跳出。
    count1=count1+1            !记录数据文件的行数。
end do

!**************************************************************************
!获取数组a(:,:)的列数
!**************************************************************************
rewind(10)                    !将当前读取位置移到数据文件开头。
read(10,"(A100)") buffer    !读取文件的第一行以获取第一行数据有多少个列数。
len=len_trim(buffer)        !取字符串的长度,不包括行尾的空格。
do N=1,len
    if(buffer(N:N)==char(44)) buffer(N:N)=char(32)    !将用分隔符逗号替换成空格。
enddo
buffer=trim(buffer)            
len=len_trim(buffer)
do N=1,len
    if(buffer(N:N)/=char(32))then
        count2=1        
        do M=N+1,len
            if(buffer(M:M)==char(32).and.buffer(M-1:M-1)/=char(32)) count2=count2+1
        enddo
        exit
    endif
enddo
allocate(a(count1,count2))    !分配数组a(:,:)的大小。
buffer="(??I6)"                !设定动态的输出格式。
buffer(2:2)=char(count2/10+48)
buffer(3:3)=char(mod(count2,10)+48)
write(*,*)"数组大小为:",count1,count2
rewind(10)
read(10,*) (a(M,:),M=1,count1)
write(06,buffer)((a(M,N),N=1,count2),M=1,count1)
write(20,buffer)((a(M,N),N=1,count2),M=1,count1)
end

3 楼

为了解决一个群里朋友的问题。

重写了几年前的一个函数,用来获得字符串里包含几个数据。

这样可以得到文件中的列数,以方便动态的读取数据。

新的函数可用空格,逗号和 TAB 间隔,且支持双引号间隔字符串。(可以修改增加其他间隔符)
[quote][font=宋体][color=#FF0000]Integer Function [/color][color=#000000]GetDataN[/color][color=#000080]( [/color][color=#000000]cStr [/color][color=#000080])
  [/color][color=#FF0000]Character[/color][color=#000080]( [/color][color=#FF0000]Len [/color][color=#000080]= * ) , [/color][color=#FF0000]Intent[/color][color=#000080]( [/color][color=#FF0000]IN [/color][color=#000080]) :: [/color][color=#000000]cStr
  [/color][color=#FF0000]Integer [/color][color=#000080]:: [/color][color=#000000]i
  [/color][color=#FF0000]Logical [/color][color=#000080]:: [/color][color=#000000]bIsSeparator [/color][color=#000080], [/color][color=#000000]bIsQuote
  GetDataN [/color][color=#000080]= [/color][color=#800080]0
  [/color][color=#000000]bIsSeparator [/color][color=#000080]= [/color][color=#808080].TRUE.
  [/color][color=#000000]bIsQuote [/color][color=#000080]= [/color][color=#808080].FALSE.
  [/color][color=#FF0000]Do [/color][color=#000000]i [/color][color=#000080]= [/color][color=#800080]1 [/color][color=#000080], [/color][color=#FF0080]Len_Trim[/color][color=#000080]( [/color][color=#000000]cStr [/color][color=#000080])
    [/color][color=#FF0000]Select Case[/color][color=#000080]( [/color][color=#000000]cStr[/color][color=#000080]([/color][color=#000000]i[/color][color=#000080]:[/color][color=#000000]i[/color][color=#000080]) )
    [/color][color=#FF0000]Case[/color][color=#000080]( [/color][color=#808080]'"' [/color][color=#000080], [/color][color=#808080]"'" [/color][color=#000080]) [/color][color=#008000]!// 如果遇到引号
      [/color][color=#FF0000]If [/color][color=#000080]( [/color][color=#808080].Not.[/color][color=#000000]bIsQuote [/color][color=#000080]) [/color][color=#000000]GetDataN [/color][color=#000080]= [/color][color=#000000]GetDataN [/color][color=#000080]+ [/color][color=#800080]1  [/color][color=#008000]!//如果不在引号中,则增加一个数据
      [/color][color=#000000]bIsQuote [/color][color=#000080]= [/color][color=#808080].Not.[/color][color=#000000]bIsQuote [/color][color=#008000]!// 引号结束或开始
      [/color][color=#000000]bIsSeparator [/color][color=#000080]= [/color][color=#808080].FALSE.
    [/color][color=#FF0000]Case[/color][color=#000080]( [/color][color=#808080]" " [/color][color=#000080], [/color][color=#808080]"," [/color][color=#000080], [/color][color=#FF0080]char[/color][color=#000080]([/color][color=#800080]9[/color][color=#000080]) ) [/color][color=#008000]!// 如果遇到分隔符
      [/color][color=#FF0000]If [/color][color=#000080]( [/color][color=#808080].Not.[/color][color=#000000]bIsQuote [/color][color=#000080]) [/color][color=#FF0000]then  [/color][color=#008000]!// 分隔符如果不在引号中
        [/color][color=#000000]bIsSeparator [/color][color=#000080]= [/color][color=#808080].TRUE.
      [/color][color=#FF0000]End If
    Case Default      
      If [/color][color=#000080]( [/color][color=#000000]bIsSeparator [/color][color=#000080]) [/color][color=#FF0000]then
        [/color][color=#000000]GetDataN [/color][color=#000080]= [/color][color=#000000]GetDataN [/color][color=#000080]+ [/color][color=#800080]1
      [/color][color=#FF0000]End If
      [/color][color=#000000]bIsSeparator [/color][color=#000080]= [/color][color=#808080].FALSE.
    [/color][color=#FF0000]End Select
  End Do
End Function [/color][color=#000000]GetDataN[/color][/font][/quote]

4 楼

事实上,这个子程序如果想通用,应该更复杂些。考虑将分隔符作为参数传入可能会降低复杂度。

看三楼的函数,首个数字前为空格或多个空格或 tab 的情况以及多个连续空格分隔的情况都没有考虑到呢。字符串去头去尾后再作循环,多个连续分隔符分隔的情况还要求 i 与 i+1 同时进行,且很可能要与前一步循环作比较。

这些死脑细胞的事,可能直接参考 awk 源码写比较方便。不知哪位能 port 过来共享下。:P

5 楼

[quote]首个数字前为空格或多个空格或 tab 的情况以及多个连续空格分隔的情况都没有考虑到呢[/quote]

谢谢,已经考虑了。连续间隔符视为一个。

6 楼

[quote][quote]首个数字前为空格或多个空格或 tab 的情况以及多个连续空格分隔的情况都没有考虑到呢[/quote]

谢谢,已经考虑了。连续间隔符视为一个。[/quote]
对,是我小题大做了。你这算法实在巧妙,赞。

EDIT:实在汗颜,之前我自己写的计算一句中的单词列数居然是同样的算法。。。
[quote]
    ! - Compute the number of Words in a string
        subroutine count_words(string, words)
            character(*), intent(in) :: string
            integer, intent(out) :: words
            character(1), parameter :: tab = char(9)
            character(1) :: curr_char
            integer :: len, i
            logical :: pre_ws

            words = 0
            pre_ws = .true.

            len = len_trim(string)
            if (len == 0) stop "countWords(): NULL string"

            do i = 1, len
                curr_char = string(i:i)
                if (curr_char /= ' ' .AND. curr_char /= tab) then
                    if (pre_ws) then
                        words = words + 1
                    end if
                    pre_ws = .false.
                else
                    pre_ws = .true.
                end if
            end do

            return
        end subroutine count_words[/quote]

我来回复

您尚未登录,请登录后再回复。点此登录或注册