Чтение строки символов неизвестной длины

Мне поручили написать программу на Фортране 95, которая будет считывать введенные символы из файла, а затем (для начала) просто выдавать их обратно. Сложность заключается в том, что эти строки ввода имеют разную длину (максимальная длина не указана), и в файле может быть любое количество строк.

я использовал

    do
      read( 1, *, iostat = IO ) DNA    ! reads to EOF -- GOOD!!
      if ( IO < 0 ) exit               ! if EOF is reached, exit do
      I = I + 1
      NumRec = I                       ! used later for total no. of records
      allocate( Seq(I) )
      Seq(I) = DNA
      print*, I, Seq(I)
      X = Len_Trim( Seq(I) )           ! length of individual sequence
      print*, 'Sequence size: ', X
      print*
    end do

Тем не менее, мой первоначальный список утверждений

    character(100), dimension(:), allocatable :: Seq
    character(100)  DNA

и соответствующие целые числа и т. д.

Я предполагаю, что я спрашиваю, есть ли способ НЕ указывать размер строк символов в первом случае. Скажем, у меня есть строка ДНК, состоящая из 200+ символов, а затем другая, состоящая только из 25. Есть ли способ, которым программа может просто прочитать то, что есть, и не нужно включать все дополнительные пробелы? Можно ли это сделать без использования len_trim, поскольку на него нельзя ссылаться в операторах объявления?


person Community    schedule 08.02.2013    source источник
comment
Есть разница между чтением части строки (в терминологии Фортрана — записи), выполнением некоторой обработки этого бита, чтением еще немного той же строки, обработкой этого бита и т. д., в отличие от чтения всей строки. а затем выполнить некоторую обработку этой полной строки (третий случай — чтение всего файла и последующая обработка всего файла). Первый в Fortran 95 прост — вы используете ввод без продвижения вперед. Второй... немного сумбурный. Какой ты хочешь? И то, и другое легко в Fortran 2003 - нужно ли придерживаться F95?   -  person IanH    schedule 08.02.2013
comment
К сожалению, у меня есть доступ только к f95, так как я использую компьютер/программное обеспечение моего руководителя и т. д. Очевидно, что простой вариант предпочтительнее, но, может быть, дайте мне оба? Спасибо!   -  person    schedule 08.02.2013


Ответы (4)


Чтобы последовательно прочитать запись в Fortran 95, используйте ввод без продвижения. Например:

CHARACTER(10) :: buffer
INTEGER :: size
READ (unit, "(A)", ADVANCE='NO', SIZE=size, EOR=10, END=20) buffer

будет считывать до 10 символов (длина буфера) при каждом вызове. Позиция в файле будет переходить к следующей записи (следующей строке) только после того, как вся запись будет прочитана серией из одного или нескольких непереходных чтений.

За исключением условия конца файла, переменная size будет определяться с фактическим количеством символов, считываемых в buffer каждый раз, когда выполняется оператор чтения.

Спецификаторы EOR и END и используются для управления потоком выполнения (выполнение переходит к оператору с соответствующей меткой), когда возникают условия конца записи или конца файла соответственно. Вы также можете использовать спецификатор IOSTAT для обнаружения этих условий, но конкретные отрицательные значения, используемые для двух условий, зависят от процессора.

Вы можете суммировать size внутри конкретной записи, чтобы вычислить длину этой конкретной записи.

Оберните такое чтение без продвижения в цикле, который правильно определяет конец файла и конец записи, и вы получите часть добавочного чтения.

В Fortran 95 спецификация длины для локальной символьной переменной должна быть выражением спецификации — по сути, выражением, которое можно безопасно вычислить до первого исполняемого оператора области, содержащего объявление переменной. Константы представляют собой простейший случай, но выражение спецификации в процедуре может включать, среди прочего, фиктивные аргументы этой процедуры.

Чтение всей записи произвольной длины представляет собой многоэтапный процесс:

  • Определите длину текущей записи, используя серию добавочных чтений. Эти добавочные чтения для конкретной записи заканчиваются, когда возникает условие конца записи, когда позиция в файле перемещается к следующей записи.
  • Backspace файл обратно к интересующей записи.
  • Вызвать процедуру, передав длину текущей записи в качестве фиктивного аргумента. Внутри этой процедуры есть символьная переменная, длина которой определяется фиктивным аргументом.
  • Внутри этой вызываемой процедуры считывайте текущую запись в эту символьную переменную, используя обычный опережающий ввод.
  • Выполните дальнейшую обработку этой символьной переменной!

Обратите внимание, что каждая запись в конечном итоге считывается дважды: один раз для определения ее длины, а второй — для фактического чтения данных в правильно «длинную» символьную переменную.

Существуют альтернативные подходы, в которых используются выделяемые (или автоматические) массивы символов единичной длины. Общая стратегия та же. В качестве примера посмотрите на код процедур Get в общей реализации ISO_VARYING_STRING.

В Фортране 2003 представлены символьные переменные с отложенной длиной, длина которых может быть указана произвольным выражением в операторе распределения или, для размещаемых переменных, длиной правой части оператора присваивания. Это (в сочетании с другими «распределяемыми» улучшениями) позволяет прогрессивному чтению, которое определяет длину записи, также создавать символьную переменную, которая содержит содержимое записи. Вашему руководителю необходимо обновить свою среду Fortran.

person IanH    schedule 08.02.2013

Вот функция для Fortran 2003, которая устанавливает выделяемую строку (InLine) точной длины входной строки (необязательно обрезанной) или возвращает .false. если конец файла

function ReadLine(aunit, InLine, trimmed) result(OK)
integer, intent(IN) :: aunit
character(LEN=:), allocatable, optional :: InLine
logical, intent(in), optional :: trimmed
integer, parameter :: line_buf_len= 1024*4
character(LEN=line_buf_len) :: InS
logical :: OK, set
integer status, size

OK = .false.
set = .true.
do
    read (aunit,'(a)',advance='NO',iostat=status, size=size) InS
    OK = .not. IS_IOSTAT_END(status)
    if (.not. OK) return
    if (present(InLine)) then
        if (set) then
            InLine = InS(1:size)
            set=.false.
        else
            InLine = InLine // InS(1:size)
        end if
    end if
    if (IS_IOSTAT_EOR(status)) exit
end do
if (present(trimmed) .and. present(InLine)) then
    if (trimmed) InLine = trim(adjustl(InLine))
end if

end function ReadLine

Например, чтобы сделать что-то со всеми строками в файле с модулем "aunit", сделайте

 character(LEN=:), allocatable :: InLine

 do while (ReadLine(aunit, InLine))
   [.. something with InLine]
 end do
person wordy    schedule 22.02.2014

Я использовал следующее. Дайте мне знать, если это лучше или хуже, чем у вас.

!::::::::::::::::::::: SUBROUTINE OR FUNCTION :::::::::::::::::::::::::::::::::::::::                                                                                                                                   
!__________________ SUBROUTINE lineread(filno,cargout,ios) __________________________                                                                                                                                   
subroutine lineread(filno,cargout,ios)                                                                                                                                                                                  
Use reallocate,ErrorMsg,SumStr1,ChCount                                                                                                                                                                                 
! this subroutine reads                                                                                                                                                                                                 
! 1. following row in a file except a blank line or the line begins with a !#*                                                                                                                                          
! 2. the part of the string until first !#*-sign is found or to end of string                                                                                                                                           
!                                                                                                                                                                                                                       
! input Arguments:                                                                                                                                                                                                      
! filno (integer)             input file number                                                                                                                                                                         
!                                                                                                                                                                                                                       
! output Arguments:                                                                                                                                                                                                     
! cargout (character)     output chArActer string, converted so that all unecessay spaces/tabs/control characters removed.                                                                                              

implicit none                                                                                                                                                                                                           
integer,intent(in)::filno                                                                                                                                                                                               
character*(*),intent(out)::cargout                                                                                                                                                                                      
integer,intent(out)::ios                                                                                                                                                                                                
integer::nlen=0,i,ip,ich,isp,nsp,size                                                                                                                                                                                   
character*11,parameter::sep='=,;()[]{}*~'                                                                                                                                                                               
character::ch,temp*100                                                                                                                                                                                                  
character,pointer::crad(:)                                                                                                                                                                                              

nullify(crad)                                                                                                                                                                                                           
cargout=''; nlen=0; isp=0; nsp=0; ich=-1; ios=0                                                                                                                                                                         
Do While(ios/=-1) !The eof() isn't standard Fortran.                                                                                                                                                                    
READ(filno,"(A)",ADVANCE='NO',SIZE=size,iostat=ios,ERR=9,END=9)ch ! start reading file                                                                                                                                  
! read(filno,*,iostat=ios,err=9)ch;                                                                                                                                                                                     
    if(size>0.and.ios>=0)then                                                                                                                                                                                           
     ich=iachar(ch)                                                                                                                                                                                                     
    else                                                                                                                                                                                                                
     READ(filno,"(A)",ADVANCE='no',SIZE=size,iostat=ios,EOR=9); if(nlen>0)exit                                                                                                                                          
    end if                                                                                                                                                                                                              
    if(ich<=32)then        ! tab(9) or space(32) character                                                                                                                                                              
        if(nlen>0)then                                                                                                                                                                                                  
     if(isp==2)then                                                                                                                                                                                                       
        isp=0;                                                                                                                                                                                                            
     else                                                                                                                                                                                                                 
        isp=1;                                                                                                                                                                                                            
     end if                                                                                                                                                                                                               
eend if; cycle;                                                                                                                                                                                                         
    elseif(ich==33.or.ich==35.or.ich==38)then !if char is comment !# or continue sign &                                                                                                                                 
     READ(filno,"(A)",ADVANCE='yes',SIZE=size,iostat=ios,EOR=9)ch; if(nlen>0.and.ich/=38)exit;                                                                                                                          
    else                                                                                                                                                                                                                
     ip=scan(ch,sep);                                                                                                                                                                                                   
     if(isp==1.and.ip==0)then; nlen=nlen+1; crad=>reallocate(crad,nlen); nsp=nsp+1; endif                                                                                                                               
     nlen=nlen+1; crad=>reallocate(crad,nlen); crad(nlen)=ch;                                                                                                                                                           
     isp=0; if(ip==1)isp=2;                                                                                                                                                                                             
    end if                                                                                                                                                                                                              
end do                                                                                                                                                                                                                  
9 if(size*ios>0)call ErrorMsg('Met error in reading file in [lineread]',-1)                                                                                                                                             
! ios<0: Indicating an end-of-file or end-of-record condition occurred.                                                                                                                                                 
if(nlen==0)return                                                                                                                                                                                                       
!write(6,'(a,l)')SumStr1(crad),eof(filno)                                                                                                                                                                               
!do i=1,nlen-1; write(6,'(a,$)')crad(i:i); end do; if(nlen>0)write(6,'(a)')crad(i:i)                                                                                                                                    
 cargout=SumStr1(crad)                                                                                                                                                                                                  
 nsp=nsp+1; i=ChCount(SumStr1(crad),' ',',')+1;                                                                                                                                                                         
if(len(cargout)<nlen)then                                                                                                                                                                                               
 call ErrorMsg(SumStr1(crad)// " is too long!",-1)                                                                                                                                                                      
!elseif(i/=nsp.and.nlen>=0)then                                                                                                                                                                                         
! call ErrorMsg(SumStr1(crad)// " has unrecognizable data number!",-1)                                                                                                                                                  
end if                                                                                                                                                                                                                  
end subroutine lineread                                                                                                                                                                                                 
person xjcode    schedule 23.09.2013

Я использую Fortran 90 для этого:

X = Len_Trim( Seq(I) )           ! length of individual sequence
write(*,'(a<X>)') Seq(I)(1:X)

Вы можете просто объявить Seq большой строкой символов, а затем обрезать ее по мере написания. Я не знаю, насколько кошерным является это решение, но оно определенно работает для моей цели. Я знаю, что некоторые компиляторы не поддерживают «выражения переменного формата», но есть различные обходные пути, позволяющие сделать то же самое почти так же просто.

обходной путь выражения переменной GNU Fortran.

person Cubby2Zeta    schedule 01.12.2015
comment
Действительно, можно объявить Seq достаточно большим, но вопрос заключается именно в том, как этого избежать. Кроме того, почему бы просто не использовать write(*, '(A)') trim(Seq(I))? - person francescalus; 01.12.2015