nullChar
nullChar

Reputation: 25

Why does my program loop twice?

I tried to create a toUpper and toLower function in Fortran, and they seemed to work fine in my implementation; the program prompts the user for an input string and then prints it out in caps and then in lowercase.

However, after I added a capitalize function, the program prompts the user for an input string twice, and I can't figure out why. It seems to work, but it does it twice.

Could someone be so kind as to let me know why my program prompts for input twice? Code and output below.

program stringutils
    character :: words*1000

    print*, 'Enter a string.'
    read(*,'(A)') words

    call toUpper(words)
    print*, 'toUpper: ', trim(words)

    call toLower(words)
    print*, 'toLower: ', trim(words)

    call capitalize(words)
    print*, 'capitalize: ', trim(words)

end program stringutils

subroutine toUpper(string)
    character :: string*1000
    integer :: i, charNum

    do i = 1, len(trim(string))
        charNum = iachar(string(i:i))
        if (charNum >= 97 .and. charNum <= 122) then
            string(i:i) = achar(charNum - 32)
        end if
    end do
end subroutine toUpper

subroutine toLower(string)
    character :: string*1000
    integer :: i, charNum

    do i = 1, len(trim(string))
        charNum = iachar(string(i:i))
        if (charNum >= 65 .and. charNum <= 90) then
            string(i:i) = achar(charNum + 32)
        end if
    end do
end subroutine toLower

subroutine capitalize(string)
    character :: string*1000
    integer :: i

    call toUpper(string(1:1))
    do i = 2, len(trim(string))
        if (iachar(string(i-1:i-1))==32) then
            call toUpper(string(i:i))
        else
            call toLower(string(i:i))
        end if
    end do
end subroutine capitalize

Example output:

 Enter a string.
this IS a tEsT!
 toUpper: THIS IS A TEST!
 toLower: this is a test!
 capitalize: This Is A Test!
 Enter a string.
why is this running a second time?
 toUpper: WHY IS THIS RUNNING A SECOND TIME?
 toLower: why is this running a second time?
 capitalize: Why Is This Running A Second Time?

I am using gfortran via MinGW in Windows.

Upvotes: 2

Views: 255

Answers (3)

Coriolis
Coriolis

Reputation: 396

I have compiled your code with Gfortran in Linux. I have experienced the following : Your "loop twice" problem indeed happened, but not everytime for me. I got a segmentation fault rather randomly. I try an answer :

You assumed right, the problem comes from the capitalize subroutine. I failed to debug your code with gdb but the seg fault seems to indicate you tried to access a component of a string that is not declared or something like that and the fact that you are calling toUpper and toLower in a loop. That's where I found something:

In the loop, you call toUpper or toLower by sending them string(i:i) which is an unique character (length 1). But in toUpper, you declared a character of length 1000 whereas in this routine you are working on string(i:i) only, that is character of length 1, that's not logical. By replacing character :: string*1000 to character :: string*1, it seems to be working and it makes more sense because this subroutine needs to convert ONE character each time.

I can't explain however why your code crashes or loops twice but it might be some kind of memory leak as the result of your character length overflow...

Anyway, @High Performance Mark gives you a good advice about assumed-length character arguments. The compiler may have been able to warn you if you have coded in a way to perform an argument check through modules and interfaces. Perhaps an INTENT clause would have been wise. These are powerful features of Fortran 90.

Upvotes: 1

High Performance Mark
High Performance Mark

Reputation: 78306

This is something of an extended comment rather than an answer ...

I'm not convinced by your explanation of the cause of the problem you initially reported. I can't see why the program would execute twice because of a mis-specified routine or two. Notwithstanding that, your routines would be much improved by a little more attention to modern Fortran practice.

You've written a bunch of routines to manipulate strings with 1000 characters. Better surely would be to write them to manipulate strings of whatever length the string passed as argument happens to be. Modern Fortran (since when I'm not sure, at least 90, possibly before that) allows passing assumed-length character arguments, rather like this:

subroutine toUpper(string)
    character(*) :: string
    integer :: i, charNum

    do i = 1, len(trim(string))
        charNum = iachar(string(i:i))
        if (charNum >= 97 .and. charNum <= 122) then
            string(i:i) = achar(charNum - 32)
        end if
    end do
end subroutine toUpper

Now, when the routine is called it will operate on all the characters in string, no more, no less.

There's a host of other modifications I'd suggest too. I don't have time to cover all of them in detail but if you poke around here on SO you'll find several Qs and As on the topics. Here's a starter set:

  • Always use explicit declaration of program entities, include implicit none in your programs (and modules). This is the fundamental number one rule for writing safe programs.
  • Put your routines into modules and use them. This ensures that the compiler can, and does, check that the calls you make to them are valid syntactically.
  • Personally I'd make all your subroutines into functions and return a version of the input string made into upper (or lower) case. There's nothing wrong with having subroutines modify the input string in place but it's easier to compose functions, for example you might one day want to write something like HumpyString = capitalise(toLower(string))

Upvotes: 1

nullChar
nullChar

Reputation: 25

Although I'm not sure what caused it to loop twice, I know it is something with the capitalize function and maybe how I'm sending a single char to a function expecting a 1000 chars. So I added charToUpper and charToLower functions and it works as expected now. Here is the new code. I feel like the capitalize function could be better... comments welcome.

program stringutils
    character words*1000

    print*, 'Enter a string.'
    read(*,'(A)') words

    call toUpper(words)
    print*, 'toUpper: ', trim(words)

    call toLower(words)
    print*, 'toLower: ', trim(words)

    call capitalize(words)
    print*, 'capitalize: ', trim(words)

end program stringutils

subroutine toUpper(string)
    character string*1000
    integer i

    do i = 1, len(trim(string))
        call charToUpper(string(i:i))
    end do
end subroutine toUpper

subroutine toLower(string)
    character string*1000
    integer i

    do i = 1, len(trim(string))
        call charToLower(string(i:i))
    end do
end subroutine toLower

subroutine charToUpper(c)
    character c
    integer charNum

    charNum = iachar(c)
    if (charNum >= 97 .and. charNum <= 122) then
        c = achar(charNum - 32)
    end if
end subroutine charToUpper

subroutine charToLower(c)
    character c
    integer charNum

    charNum = iachar(c)
    if (charNum >= 65 .and. charNum <= 90) then
        c = achar(charNum + 32)
    end if
end subroutine charToLower

subroutine capitalize(string)
    character :: string*1000
    integer :: i

    call charToUpper(string(1:1))
    do i = 2, len(trim(string))
        if (iachar(string(i-1:i-1))==32) then
            call charToUpper(string(i:i))
        else
            call charToLower(string(i:i))
        end if
    end do
end subroutine capitalize

Upvotes: 0

Related Questions