【问题标题】:Are there alternate implementations of GNU getline interface?是否有 GNU getline 接口的替代实现?
【发布时间】:2010-10-18 14:52:30
【问题描述】:

我目前正在进行的实验使用具有复杂源历史且没有明确定义的许可证的软件库。将事物合理化并在固定许可下发布将是一项相当大的工作。

它还打算运行一个随机的 unixish 平台,只有我们支持的一些 libc 有 GNU getline,但现在代码需要它。

有谁知道在限制较少的许可下可以重新实现GNU getline 语义?

编辑::我问是因为谷歌没有帮助,我想尽可能避免写一个(这可能是一个有趣的练习,但它不是最好的用途我的时间。)

更具体地说,有问题的接口是:

ssize_t getline (char **lineptr, size_t *n, FILE *stream);

【问题讨论】:

标签: c licensing getline


【解决方案1】:

我很困惑。

我查看了链接,阅读了说明,这是一个很好的实用程序。

但是,您是说您根本无法将此函数重写为规范吗?规范似乎很清楚,

这里:

/* This code is public domain -- Will Hartung 4/9/09 */
#include <stdio.h>
#include <stdlib.h>

size_t getline(char **lineptr, size_t *n, FILE *stream) {
    char *bufptr = NULL;
    char *p = bufptr;
    size_t size;
    int c;

    if (lineptr == NULL) {
        return -1;
    }
    if (stream == NULL) {
        return -1;
    }
    if (n == NULL) {
        return -1;
    }
    bufptr = *lineptr;
    size = *n;

    c = fgetc(stream);
    if (c == EOF) {
        return -1;
    }
    if (bufptr == NULL) {
        bufptr = malloc(128);
        if (bufptr == NULL) {
            return -1;
        }
        size = 128;
    }
    p = bufptr;
    while(c != EOF) {
        if ((p - bufptr) > (size - 1)) {
            size = size + 128;
            bufptr = realloc(bufptr, size);
            if (bufptr == NULL) {
                return -1;
            }
        }
        *p++ = c;
        if (c == '\n') {
            break;
        }
        c = fgetc(stream);
    }

    *p++ = '\0';
    *lineptr = bufptr;
    *n = size;

    return p - bufptr - 1;
}

int main(int argc, char** args) {
    char *buf = NULL; /*malloc(10);*/
    int bufSize = 0; /*10;*/

    printf("%d\n", bufSize);
    int charsRead =  getline(&buf, &bufSize, stdin);

    printf("'%s'", buf);
    printf("%d\n", bufSize);
    return 0;
}

15 分钟,我已经有 10 年没有写过 C 语言了。它轻微破坏了 getline 合同,因为它只检查 lineptr 是否为 NULL,而不是 NULL 和 n == 0。如果你愿意,你可以修复它。 (另一种情况对我来说没有多大意义,我想在那种情况下你可以返回 -1。)

将 '\n' 替换为变量以实现“getdelim”。

人们还写代码吗?

【讨论】:

  • 这适用于短字符串,但在重新分配后可能会失败。 bufptr 可能会获得一个新地址,并且 p 需要保持相同的相对偏移量。在我的测试中(使用 MinGW),realloc 可能会使用同一个指针多次返回(如果该位置恰好有足够的内存),或者可能会在第一次重新分配时返回一个新地址。新地址可以在内存中或远处,也可以在第一个地址之前和之后。 IE它可以使p成为一个随机数。要修复,请输入“offset = p - bufptr;”在 while EOF 行下,以及“p = bufptr + offset;”在 if NULL 块之后。
  • ((p - bufptr) &gt; (size - 1)) 是一个问题,如果size == 0 (并且*lineptr 不典型地非NULL)因为size - 1 是一个大 数字。建议((p - bufptr + 1) &gt; size).
  • malloc 和 realloc 在我的 stdio.h 代码 void* 指针上返回。所以我不得不为两行添加强制转换运算符,也包括 (char*)。
  • 您不能在 size_t 中返回 -1。如果出现问题,这将非常失败。
  • 请注意,@Todd 指出,此 getline 实现非常糟糕。 不要在任何地方使用。
【解决方案2】:

如果您正在为 BSD 编译,请使用 fgetln 而是

【讨论】:

    【解决方案3】:

    使用来自 NetBSD 的这些可移植版本:getdelim() 和 getline()

    这些来自 pkgsrc 中的 libnbcompat,并且在每个文件的顶部都有一个 BSD 许可证。您需要两者,因为 getline() 调用 getdelim()。获取两个文件的最新版本。请参阅每个文件顶部的 BSD 许可证。修改文件以适合您的程序:您可能需要在其中一个头文件中声明 getline() 和 getdelim(),并修改这两个文件以包含您的头文件而不是 nbcompat 头文件。

    这个版本的 getdelim() 是可移植的,因为它调用 fgetc()。相比之下,来自 libc(如 BSD libc 或 musl libc)的 getdelim() 可能会使用该 libc 的私有功能,因此它不能跨平台工作。

    在POSIX 2008 specified getline() 之后的几年里,更多的 Unixish 平台添加了 getline() 函数。 getline() 很少丢失,但它仍然可以在旧平台上发生。一些人尝试在旧平台(如 PowerPC Mac OS X)上引导 NetBSD pkgsrc,因此他们希望 libnbcompat 提供缺少的 POSIX 函数,如 getline()。

    【讨论】:

      【解决方案4】:

      Will Hartung 的代码存在一个非常严重的问题。 realloc 很可能会释放旧块并分配一个新块,但代码中的 p 指针将继续指向原始块。这个尝试通过使用数组索引来解决这个问题。它还试图更接近地复制标准 POSIX 逻辑。

      /* The original code is public domain -- Will Hartung 4/9/09 */
      /* Modifications, public domain as well, by Antti Haapala, 11/10/17
         - Switched to getc on 5/23/19 */
      
      #include <stdio.h>
      #include <stdlib.h>
      #include <errno.h>
      #include <stdint.h>
      
      // if typedef doesn't exist (msvc, blah)
      typedef intptr_t ssize_t;
      
      ssize_t getline(char **lineptr, size_t *n, FILE *stream) {
          size_t pos;
          int c;
      
          if (lineptr == NULL || stream == NULL || n == NULL) {
              errno = EINVAL;
              return -1;
          }
      
          c = getc(stream);
          if (c == EOF) {
              return -1;
          }
      
          if (*lineptr == NULL) {
              *lineptr = malloc(128);
              if (*lineptr == NULL) {
                  return -1;
              }
              *n = 128;
          }
      
          pos = 0;
          while(c != EOF) {
              if (pos + 1 >= *n) {
                  size_t new_size = *n + (*n >> 2);
                  if (new_size < 128) {
                      new_size = 128;
                  }
                  char *new_ptr = realloc(*lineptr, new_size);
                  if (new_ptr == NULL) {
                      return -1;
                  }
                  *n = new_size;
                  *lineptr = new_ptr;
              }
      
              ((unsigned char *)(*lineptr))[pos ++] = c;
              if (c == '\n') {
                  break;
              }
              c = getc(stream);
          }
      
          (*lineptr)[pos] = '\0';
          return pos;
      }
      

      可以通过锁定流一次并使用getc_unlocked(3) 的等效项来提高平台的性能 - 但这些在 C 中没有标准化;如果您使用的是 POSIX 版本,那么您可能已经拥有getline(3)。

      【讨论】:

      • 我收到此错误:error: invalid conversion from ‘void*’ to ‘char*’ [-fpermissive] 代表您的 malloc(128) 和 realloc(*lineptr, new_size)。我通过将它们转换为 (char*): invalid conversion from void*' to char*' when using malloc? 来修复它
      • 当我用 Cygwin C 测试时,性能比内置的 getline() 差 10 倍
      • 至于性能,这是预期的,因为我使用的是fgetc,它需要锁定每个读取字符的流。不幸的是,没有符合标准的方法来避免锁定解锁。有 POSIX,但如果你有 POSIX,你可能也会有 getline。
      • @user on Windows 你可以使用_lock_file 和_getc_nolock
      【解决方案5】:

      尝试使用 fgets() 代替 getline()。我在 Linux 中使用 getline() 并且在我迁移到 Windows 之前它运行良好。 Visual Studio 无法识别 getline()。因此,我将字符指针替换为字符,并将 EOF 替换为 NULL。见下文:

      #define CHARCOUNT 1000
      

      之前:

      char *line = (char*) malloc(CHARCOUNT);
      size_t size;
      FILE *fp = fopen(file, "r");
      while(getline(&line, &size, fp) != -1) {
         ...
      }
      free(line);
      

      之后:

      char line[CHARCOUNT];
      while(fgets(line, CHARCOUNT, fp) != NULL) {
         ...
      }
      

      【讨论】:

      • 这将不会处理超过传递给fgets() 的最大长度的行,超过该长度的行将被拆分。所以它显着改变了程序的语义。使用getline() 的代码隐含地希望能够读取任何合理长度的行,因此将getline() 替换为fgets() 是一个潜在的错误。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-10-26
      • 1970-01-01
      • 1970-01-01
      • 2019-04-07
      • 1970-01-01
      • 2013-06-08
      • 2014-12-10
      相关资源
      最近更新 更多