有没有一种快速的方法来获取放入数组中的最后一个元素?

Is there a quick way to get the last element that was put in an array?

我使用 fgetsstdin 中读取一行并将其保存在 char 数组中,我想获取我写的行的最后一个字母,应该在\n[=15=].

之前的数组中

例如,如果我有一个 char line[10] 并在终端 1stLine 上写,有没有一种快速获取字母 e 的方法,而不是仅仅循环到它?

我看到了这个 post 但我认为它对我不起作用,即使我只是创建数组而不用 fgets 填充它,sizeof line 已经是 10 因为数组中已经有东西

我知道这不是 java 而且我不能只是 .giveMeLastItem(),但我想知道是否有比循环直到 \n 之前的字符更聪明的方法来获得我写的最后一封信

代码类似于

char command[6];
fgets(command,6,stdin);

如果您知道标记值,例如:[=10=](或 \n,或与此相关的任何值),并且您想要紧接在其之前的元素的值,您可以

  • 使用strchr()找出哨兵的位置并
  • 获取 retPtr-1 的地址并取消引用以获得您想要的值。

一旦你只有数组,就没有其他方法可以做到这一点。您可以使用 strlen(line) 然后根据该索引获取最后一个字符的位置,但这基本上完全相同(遍历数组)。

char lastChar = line[strlen(line)-1];

时间复杂度为 O(n),其中 n 是输入长度。

您可以将输入法改为char by char input并计算长度或存储最后一次输入。像这样的每个 O(1) 方法之前都使用 O(n) 时间(就像你读取的每个字符的 n 次 O(1))。但是除非你必须真正加速优化(当你使用用户输入时你不需要)应该只使用像 strlen(line) 这样的函数遍历数组(并存储结果,当你多次使用它时).

编辑: strchr() 函数 Sourav Ghosh 提到的功能完全相同,但你 can/must 指定终止字符。

有许多不同的方法可以检查 fgets() 读取的行:

  • 首先你应该检查 fgets() 的 return 值: NULL 的 return 值意味着到达文件末尾或发生某种错误,目标数组的内容未定义。也建议使用更长的数组。

      char command[80];
      if (fgets(command, sizeof command, stdin) == NULL) {
          // end of file or read error
          return -1;
      }
    
  • 你可以用len = strlen(command)计算字符数,如果这个长度os不为零(*),command[len - 1]是从文件,如果该行少于 5 个字节,它应该是 '\n'。剥离换行需要测试:

      size_t len = strlen(command);
      if (len > 0 && command[len - 1] == '\n')
          command[--len] = '[=11=]';
    
  • 你可以使用strchr()来定位换行符,如果有char *p strchr(command, '\n');如果有换行符,你可以这样去掉它:

      char *p = strchar(command, '\n');
      if (p != NULL)
          *p = '[=12=]';
    
  • 你也可以用pos = strcspn(command, "\n")统计"\n"集合中的字符数。 pos 将指向换行符或空终止符。因此你可以去掉尾随的换行符:

      command[strcspn(command, "\n")] = '[=13=]';  // strip the newline if any
    
  • 你也可以写一个简单的循环:

      char *p = command;
      while (*p && *p != '\n')
          p++;
      *p = '\n'; // strip the newline if any
    

(*) strlen(command) 可以 return 0 如果文件在行首包含嵌入的空字符。 fgets() 将空字节视为普通字符,它会继续将字节读入数组,直到读取 size - 1 个字节或读取一个换行符。

一个简单的方法可以看成下面的方式

char last_letter = command[ strcspn( command, "\n" ) - 1 ];

前提是字符串不为空或只包含换行符 '\n'.

这是一个演示程序。

#include <stdio.h>
#include <string.h>

int main(void) 
{
    enum { N = 10 };
    char command[N];
    
    while ( fgets( command, N, stdin ) && command[0] != '\n' )
    {
        char last_letter = command[ strcspn( command, "\n" ) - 1 ];
        printf( "%c ", last_letter );
    }
    
    putchar( '\n' );
    
    return 0;
}

如果要输入下面的字符串序列

Is
there
a
quick
way
to
get
the
last
element
that
was
put
in
an
array?

那么输出将是

s e a k y o t e t t t s t n n ? 

最快的方法是像这样保留一个引用数组:

long ref[]

和 ref[x] 包含第 x 行最后一个字符的文件偏移量。将此引用保存在文件的开头,您将执行如下操作:

fseek(n*sizeof(long))
long ref = read_long()
fseek(ref)
read_char()

我认为这是读取第 n 行末尾最后一个字符的最快方法。

除了其他好的例子。

另一种方法是使用 fscanf()/scanf()%n 格式说明符将输入字符串后到目前为止读取的字符数写入参数。

然后你把这个数减一,然后用它作为 command:

的索引
char command[6];
int n = 0;

if (fscanf(stdin, "%5[^\n]" "%n", command, &n) != 1)
{
    fputs("Error at input!", stderr);
    // error routine.
}

getchar();

if (n != 0)
{
    char last_letter = command[n-1];
}

#include <stdio.h>

int main (void)
{
    char command[6];
    int n = 0;

    if (fscanf(stdin, "%5[^\n]" "%n", command, &n) != 1)
    {
        fputs("Error at input!", stderr);
        // error routine.
    }

    getchar();
    
    if (n != 0)
    {
        char last_letter = command[n-1];
        putchar(last_letter);
    }

    return 0;
}

执行:

./a.out
hello
o

我对上述三种从流中读取一行并测量其长度的方法进行了快速测试。我读 /usr/share/dict/words 100 次并用 clock()/1000:

测量
fgets + strlen = 420
getc = 510
fscanf with " 100[^\n]%n" = 940

这是有道理的,因为 fgets 和 strlen 只执行 2 次调用,getc 每个字符执行一次调用,而 fscanf 可能执行一次调用,但要设置很多机制来处理复杂的格式,因此开销更多。请注意 fscanf 格式中添加的 space 以跳过上一行留下的换行符。