Заполнить буфер символов по частям - PullRequest
2 голосов
/ 07 мая 2019

В Фортране 2008 года я пытаюсь заполнить буфер большого числа символов по частям эффективным и читабельным способом.

Для фона код предназначен для процедуры вывода кода молекулярной динамики. Он должен создать содержимое для выходного файла, содержащего свойства атома. Точный формат каждого свойства зависит от его значения. Содержимое буферизуется и записывается в один вызов с использованием MPI-IO, поэтому просто запись непосредственно в файл невозможна. Можно предположить, что буфер достаточно большой.

В C этого было бы просто достичь с помощью sprintf, который возвращает количество записанных байтов. Затем его можно использовать для вычисления места написания следующего фрагмента.

В Фортране я не знаю, как узнать количество символов, записанных оператором write. Это означает, что я должен использовать уродливый и хрупкий код, такой как:

integer :: nchars, len, iatom
character(len=SOMETHING_LARGE) :: strbuf

nchars = 0

! In a loop of iatom = 1, numatoms
! Many similar writes, with different cases for the format
! based on the actual values.
len = 3 * (1 + 11)
write(strbuf(nchars+1 : nchars+len), &
      '(3(1x,f11.7))') x(:, iatom)
nchars = nchars + len

Обратите внимание, что я должен вручную вычислить длину фрагмента, записанного в буфер, поскольку отсутствие верхней границы означает, что остаток буфера (200 МБ) дополняется пробелами. Это означает, что я не могу использовать форматы, которые автоматически определяют количество используемых цифр. Кроме того, код нарушается, если формат изменяется без изменения len. Кроме того, я считаю уродливым, что len должен быть передан в качестве конечной точки среза строки.

Я разработал обходной путь, обернув C sprintf, используя bind(C). Что делает это очень уродливым, так это то, что поскольку функции vararg явно исключены из bind(C) стандартом Фортрана, я должен сначала написать обертку без vararg в C отдельно для каждой комбинации типов параметров (например, 'string, real, real, int , real ',' string, string, real, real, real, int ', ...), а затем напишите bind(C) интерфейс для каждого из них в Fortran. Хотя это немного менее хрупко (и почему-то на 50% быстрее), оно все же безобразно, тем более что в нем смешаны С и Фортран.

Для сравнения, тот же код в чистом C:

char *buf = ...;
char *ptr = buf;
...
ptr += sprintf(ptr, "%g %g %g", x[3*i], x[3*i+1], x[3*i+2]);

Есть ли способ добиться такой же ясности и надежности в Фортране?

1 Ответ

0 голосов
/ 07 мая 2019

Вы можете позволить компилятору / процессу отслеживать длину

character(len=37) strtmp
character(len=:), allocatable :: strbuf
strbuf = ''
!
! In your loop...
!
   write(strmp, '(3(1X,F11.7))') x(:,iatom)
   strbuf = trim(strbuf) // strtmp
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...