unquote(3f) - [M_strings:QUOTES] remove quotes from string as if
read with list-directed input
(LICENSE:PD)
function unquote(quoted_str,esc) result (unquoted_str)
character(len=*),intent(in) :: quoted_str
character(len=1),optional,intent(in) :: esc
character(len=:),allocatable :: unquoted_str
Remove quotes from a CHARACTER variable as if it was read using
list-directed input. This is particularly useful for processing
tokens read from input such as CSV files.
Fortran can now read using list-directed input from an internal file,
which should handle quoted strings, but list-directed input does not
support escape characters, which UNQUOTE(3f) does.
quoted_str input string to remove quotes from, using the rules of
list-directed input (two adjacent quotes inside a quoted
region are replaced by a single quote, a single quote or
double quote is selected as the delimiter based on which
is encountered first going from left to right, ...)
esc optional character used to protect the next quote
character from being processed as a quote, but simply as
a plain character.
unquoted_str The output string, which is based on removing quotes
from quoted_str.
Sample program:
program demo_unquote
use M_strings, only : unquote
implicit none
character(len=128) :: quoted_str
character(len=:),allocatable :: unquoted_str
character(len=1),parameter :: esc='\'
character(len=1024) :: msg
integer :: ios
character(len=1024) :: dummy
do
write(*,'(a)',advance='no')'Enter test string:'
read(*,'(a)',iostat=ios,iomsg=msg)quoted_str
if(ios /= 0)then
write(*,*)trim(msg)
exit
endif
! the original string
write(*,'(a)')'QUOTED ['//trim(quoted_str)//']'
! the string processed by unquote(3f)
unquoted_str=unquote(trim(quoted_str),esc)
write(*,'(a)')'UNQUOTED ['//unquoted_str//']'
! read the string list-directed to compare the results
read(quoted_str,*,iostat=ios,iomsg=msg)dummy
if(ios /= 0)then
write(*,*)trim(msg)
else
write(*,'(a)')'LIST DIRECTED['//trim(dummy)//']'
endif
enddo
end program demo_unquote
John S. Urban
Public Domain
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
character(len=*), | intent(in) | :: | quoted_str | |||
character(len=1), | intent(in), | optional | :: | esc |
function unquote(quoted_str,esc) result (unquoted_str)
character(len=*),intent(in) :: quoted_str ! the string to be unquoted
character(len=1),optional,intent(in) :: esc ! escape character
character(len=:),allocatable :: unquoted_str
integer :: inlen
character(len=1),parameter :: single_quote = "'"
character(len=1),parameter :: double_quote = '"'
integer :: quote ! whichever quote is to be used
integer :: before
integer :: current
integer :: iesc
integer :: iput
integer :: i
logical :: inside
!-----------------------------------------------------------------------------------------------------------------------------------
if(present(esc))then ! select escape character as specified character or special value meaning not set
iesc=iachar(esc) ! allow for an escape character
else
iesc=-1 ! set to value that matches no character
endif
!-----------------------------------------------------------------------------------------------------------------------------------
inlen=len(quoted_str) ! find length of input string
allocate(character(len=inlen) :: unquoted_str) ! initially make output string length of input string
!-----------------------------------------------------------------------------------------------------------------------------------
if(inlen >= 1)then ! double_quote is the default quote unless the first character is single_quote
if(quoted_str(1:1) == single_quote)then
quote=iachar(single_quote)
else
quote=iachar(double_quote)
endif
else
quote=iachar(double_quote)
endif
!-----------------------------------------------------------------------------------------------------------------------------------
before=-2 ! initially set previous character to impossible value
unquoted_str(:)='' ! initialize output string to null string
iput=1
inside=.false.
STEPTHROUGH: do i=1,inlen
current=iachar(quoted_str(i:i))
if(before == iesc)then ! if previous character was escape use current character unconditionally
iput=iput-1 ! backup
unquoted_str(iput:iput)=char(current)
iput=iput+1
before=-2 ! this could be second esc or quote
elseif(current == quote)then ! if current is a quote it depends on whether previous character was a quote
if(before == quote)then
unquoted_str(iput:iput)=char(quote) ! this is second quote so retain it
iput=iput+1
before=-2
elseif(.not.inside.and.before /= iesc)then
inside=.true.
else ! this is first quote so ignore it except remember it in case next is a quote
before=current
endif
else
unquoted_str(iput:iput)=char(current)
iput=iput+1
before=current
endif
enddo STEPTHROUGH
!-----------------------------------------------------------------------------------------------------------------------------------
unquoted_str=unquoted_str(:iput-1)
!-----------------------------------------------------------------------------------------------------------------------------------
end function unquote