Чтение и запись в файл в fortran95

Я пытаюсь сделать следующее в Fortran:

  1. Чтение текстового документа построчно
  2. Кодируйте каждую строку слово за словом, «заменяя» каждую букву на ту, которая идет на l позиций после нее в алфавите, l = длина слова
  3. Запишите закодированную строку в новый выходной документ

Мой цикл, кажется, работает бесконечно, и в выходные документы не записываются слова.

Мой тестовый входной документ состоит всего из 2 строк:

  • некоторые вещи
  • еще кое-что

Первая строка читается и кодируется правильно

PROGRAM zeichen
USE PufferMod
IMPLICIT NONE 
CHARACTER(LEN=132) :: z
CHARACTER(maxlen):: x
INTEGER:: a,e,i,l


OPEN(UNIT=39,FILE="intext.txt",ACTION="READ", STATUS="OLD")
OPEN(UNIT=40, FILE="outext.txt", ACTION="WRITE")

DO 
2 READ(39,"(A)", end=10) z
    a=1;
    DO WHILE(a<133)
     WRITE(*,*) z
     WRITE(*,*) a
     CALL Suche_Wort(z,a,e)
         l=e-a+1
         WRITE(*,*) a,e,l
                DO i=a,e
                    IF(z(i:i)/="") THEN
                    CALL Codiere(z(i:i),l)
                    END IF
                END DO
                a=e+1
    END DO 
 WRITE(40,*) z
 END DO
10 CLOSE(UNIT=39)
   CLOSE(UNIT=40)

END PROGRAM zeichen









    MODULE PufferMod
IMPLICIT NONE
PRIVATE
PUBLIC :: maxlen, Codiere, Suche_Wort

INTEGER, PARAMETER :: maxlen = 132

CONTAINS

FUNCTION Kleinbuchstabe(z)
    CHARACTER(LEN=1) :: z
    LOGICAL :: Kleinbuchstabe
    Kleinbuchstabe= "a" <= z .AND. z <= "z"
END FUNCTION Kleinbuchstabe

FUNCTION Grossbuchstabe(z)
    CHARACTER(LEN=1) :: z
    LOGICAL :: Grossbuchstabe
    Grossbuchstabe= "A" <= z .AND. z <= "Z"
END FUNCTION Grossbuchstabe

FUNCTION Buchstabe(z)
    CHARACTER(LEN=1) :: z
    LOGICAL :: Buchstabe
    Buchstabe=  (("a" <= z .AND. z <= "z") .OR. ("A" <= z .AND. z <= "Z"))
END FUNCTION Buchstabe

SUBROUTINE Codiere(z, Verschiebung)
    CHARACTER(LEN=1) :: z
    INTEGER, INTENT(IN) :: Verschiebung
    INTEGER :: CodevonA
    IF ( Kleinbuchstabe(z) ) THEN
    CodevonA= ICHAR("a")
    ELSE IF ( Grossbuchstabe(z) ) THEN
    CodevonA= ICHAR("A")
    END IF
    z= CHAR( CodevonA + MOD( ICHAR(z) - CodevonA + Verschiebung, 26 ) )
END SUBROUTINE Codiere

SUBROUTINE Suche_Wort(z,a,e)
CHARACTER(LEN=maxlen), INTENT(in):: z
INTEGER, INTENT(inout):: a
INTEGER, INTENT(out):: e
CHARACTER:: x, temp*1
INTEGER:: i
i=1
temp=z(a:a)
!WRITE(*,*) "a before loop",a, "temp", temp
 DO WHILE(temp == " ")    !Find start of next word
    temp=z(a+i:a+i)
    i=i+1
    a=a+1
 END DO
 !WRITE(*,*) "a after loop", a
 e=a+1
 i=1
 temp=z(e:e)
DO WHILE(temp/= "")      !find where the word ends by finding a space
    temp=z(e+i:e+i)
    i=i+1
    e=e+1
    IF(temp == " ") EXIT
END DO
END SUBROUTINE Suche_Wort

END MODULE PufferMod

person Community    schedule 06.08.2018    source источник
comment
Я думал, что это закончится, когда будет прочитана последняя строка; когда входной документ заканчивается.   -  person    schedule 06.08.2018
comment
10 — это то, куда идти, когда входной документ полностью прочитан.   -  person    schedule 06.08.2018
comment
Когда я запускаю ваш код, я получаю ошибку сегментации, когда код выходит за пределы z. Попробуйте перекомпилировать с включенной проверкой границ ошибок и попробуйте еще раз.   -  person High Performance Mark    schedule 06.08.2018
comment
Как я могу это сделать?   -  person    schedule 06.08.2018
comment
Вы обращаетесь к документации вашего компилятора, ищете фразу границы массива или проверка границ. Или посмотрите другие вопросы здесь, на SO, тема освещается примерно раз в неделю.   -  person High Performance Mark    schedule 06.08.2018


Ответы (2)


Ваша подпрограмма Suche_Wort имеет как минимум три проблемы:

  • Он никогда не проверяет, находятся ли индексы переменной z в допустимых пределах.

  • Предполагается, что конечный индекс строго больше начального индекса, поэтому не допускается односимвольное слово.

  • В каждом из своих циклов он увеличивает на единицу как i, так и другую целочисленную переменную (a в первом цикле, e во втором цикле), фактически увеличивая индекс z на 2 на каждой итерации.

Я бы рекомендовал внести два изменения в программу zeichen, чтобы избавиться от завершающих пробелов, используя встроенные функции trim (удалить завершающие пробелы) и len_trim (вычислить длину обрезанной переменной):

2 READ(39,"(A)", end=10) z
    a=1;
    DO WHILE(a<=len_trim(z))
     WRITE(*,*) z
     WRITE(*,*) a
     CALL Suche_Wort(trim(z),a,e)
!...

Затем проверки и исправления могут быть добавлены в подпрограмму Suche_Wort (более просто, чем если бы нам пришлось беспокоиться о возможности того, что все символы, оставшиеся в z(a:), были пробелами):

SUBROUTINE Suche_Wort(z,a,e)
  CHARACTER(LEN=*), INTENT(in):: z ! Dummy argument has length of actual argument
  INTEGER, INTENT(inout):: a
  INTEGER, INTENT(out):: e
  CHARACTER(len=1):: temp
  temp=z(a:a)
  DO WHILE((temp == " ").and.(a<len(z)))    !Find start of next word
    temp=z(a+1:a+1)
    a=a+1
  END DO
  e=a                              ! Allows 1-character words
  DO WHILE((temp/= " ").and.(e<len(z)))      !find where the word ends by finding a space
    temp=z(e+1:e+1)
    e=e+1
  END DO
END SUBROUTINE Suche_Wort
person jme52    schedule 06.08.2018
comment
После применения ваших изменений и немного больше, он работает почти идеально. Единственная оставшаяся проблема - вывод на той же строке - person ; 07.08.2018
comment
Извините, эта проблема возникает только в блокноте, а не в блокноте ++. - person ; 07.08.2018

Зачем вообще думать о словах? Просто конвертируйте A:Z или a:z.

   PROGRAM zeichen
     CHARACTER(LEN=132) :: z
     INTEGER :: iostat, Verschiebung = 1

     OPEN (UNIT=39, FILE="ziechen.f90", ACTION="READ", STATUS="OLD")
     OPEN (UNIT=40, FILE="outext.txt", STATUS="UNKNOWN")

      DO 
         READ (39,fmt="(A)", iostat=iostat) z
         if ( iostat /= 0 ) exit

         call convert_line ( z, Verschiebung )

         WRITE (40,fmt="(A)") trim (z)
         WRITE ( *,fmt="(A)") trim (z)
      END DO
   END PROGRAM zeichen

    Subroutine convert_line ( z, inc )
      character*(*) z
      integer   inc, i
      do i = 1, len_trim (z)
        call convert_character ( z(i:i), inc )
      end do
    end Subroutine convert_line

    Subroutine convert_character ( z, inc )
      character z
      integer   inc, ia, ic
!
      if      ( z >= 'A' .and. z <= 'Z' ) then
        ia = ichar ('A')
      else if ( z >= 'a' .and. z <= 'z' ) then
        ia = ichar ('a')
      else
        return
      end if
      ic = mod ( ichar (z) - ia + inc + 26, 26 )
      z  = char (ia + ic )
!
    end Subroutine convert_character
person johncampbell    schedule 11.08.2018
comment
Потому что я меняю каждое слово на его длину, а не на фиксированную сумму - person ; 11.08.2018
comment
Моя ошибка, я принял l за 1 при чтении кода. Изменение, которое я сделал для convert_character, может иметь некоторое значение, в зависимости от набора символов, доступного для слов. - person johncampbell; 11.08.2018
comment
Я все равно это ценю; любой более хорошо написанный код полезен для меня. - person ; 11.08.2018
comment
Если у вас есть массив CHARACTER z, содержащий слово, вы можете преобразовать его с помощью z = merge(achar(iachar(z)+modulo(iand(iachar(z),NOT(32))-65+size(z),26)- (iand(iachar(z),NOT(32))-65)),z,BLT(iand(iachar(z),NOT(32))-65,26)) - person user5713492; 15.08.2018
comment
@user5713492 user5713492 Я действительно не понимаю, чем привлекает такое сложное решение. Я всегда рекомендую разбивать проблему на более мелкие части, которые легче понять. - person johncampbell; 15.08.2018