如何创建一个从文件中获取值的函数,使它们成为结构数组并返回它?

How to create a function that gets values from file, make them an struct array and return it?

提问人:Alperen İsa 提问时间:7/15/2023 最后编辑:Alperen İsa 更新时间:7/15/2023 访问量:48

问:

我有一个名为languages.c的源文件。我想读取 get_availabile_languages () 的数据/语言.txt文本并创建一个语言结构数组并在每个元素中定义它们。

但是,当我尝试执行此操作时,我收到 SIGSEGV 错误。 我创建的代码旨在执行以下操作。

  1. 开始计算行数。创建一个元素,每行都有一个结构。languagesLanguage
  2. 用于在符号处拆分字符串。将第一个赋值给变量,将第二个赋值给 。strtok ()data/languages.txt=lang_codelang_name
  3. 完成后,返回结构数组。
/* main.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "language.h"

int i;

int main ()
{
  puts ("--- Welcome to PiluX CLI installer ---");
  puts ("Language: ");
  // char* lang_name = get_language_name_from_code ("en");
  // char* lang_code = get_language_code_from_name ("English");
  // printf ("%s\n", lang_name);
  // printf ("%s\n", lang_code);
  Language *languages = get_available_languages ();
  printf ("%s\n", languages[0].name);
  free (languages);

  return 0;
}
/* language.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <piluxsetup/language.h>


FILE* _open_languages_file ()
{
  /* 
    data/languages.txt dosyasını açar.
    Kodun tekrar tekrar yazılmaması için oluşturuldu.
  */
  FILE *supported_languages = fopen ("data/languages.txt", "r");
  
  if (!supported_languages)
  {
    printf ("\033[1;91mhata:\033[0m Language list can't open: %s\n", strerror(errno));
    exit (1);
  }

  return supported_languages;
}

char* get_language_name_from_code (char* code)
{
  /*
    Dil kodları arasından en yakın eşleşmeyi döndürür.
    Örnek:
    get_language_from_code ("de_DE"); -> Deutsh
  */

  FILE *supported_languages = _open_languages_file ();

  char *line;
  while (fgets (line, 80, supported_languages) != NULL)
  {
    if (strstr (line, code))
    {
      char* tokenizer = strtok (line, "="); // Birinci elemanı döndürür.
      return tokenizer;
      fclose(supported_languages);
    }
  }
  fclose(supported_languages);
  return "(not specified)";
}

char* get_language_code_from_name (char* name)
{
  /*
    Dil adları arasından en yakın eşleşmeyi döndürür.
    Örnek:
    get_language_from_name ("Türkçe"); -> tr_TR
  */

  FILE *supported_languages = _open_languages_file ();

  char *line;
  while (fgets (line, 80, supported_languages) != NULL)
  {
    if (strstr (line, name))
    {
      char* tokenizer = strtok (line, "=");
      tokenizer = strtok (NULL, "="); // sonraki parçayı işle
      tokenizer[strlen (tokenizer) - 1] = '\0'; // newline karakterini sil
      fclose(supported_languages);
      return tokenizer;
    }
  }
  fclose(supported_languages);
  return "(not specified)";
}

Language* get_available_languages ()
{
  FILE *supported_languages = _open_languages_file ();

  int line_count = 0;
  char line[80];

  char* lang_code = "";
  char* lang_name = "";
  Language* languages = malloc (sizeof (Language)*2);

  while (fgets (line, 80, supported_languages) != NULL)
  {
    ++line_count;
    char* tokenizer = strtok (line, "=");
    lang_code = tokenizer;
    tokenizer = strtok (NULL, "="); // sonraki parçayı işle
    lang_name = tokenizer;
    tokenizer[strlen (tokenizer) - 1] = '\0'; // newline karakterini sil

    languages = realloc (languages, line_count);

    strcpy (languages[line_count - 1].name, lang_name);
    strcpy (languages[line_count - 1].code, lang_code);
  }
  return languages;
}
/* language.h */
typedef struct language
{
  char* name;
  char* code;
} Language;

char* get_language_name_from_code (char* code);
char* get_language_code_from_name (char* name);
Language* get_available_languages ();
# data/languages.txt
tr_TR=Türkçe
en_US=English

输出:

[alperen@LAPTOP-EDN1J28M setup]$ make clean && make
Artıklar temizleniyor...
rm -rf lib/* *.o piluxsetup
gcc -c language.c -Iincludes -Llib -lsetup  -fPIC
gcc -c partition.c -Iincludes -Llib -lsetup  -fPIC
gcc language.o partition.o -Iincludes  -shared -o lib/libpiluxsetup.so
gcc -c main.c -Iincludes -Llib -lpiluxsetup  -fPIC
gcc main.o -Llib -lpiluxsetup -Iincludes  -o setup
[alperen@LAPTOP-EDN1J28M distsetup]$ ./setup 
--- Welcome to installer ---
Language: 
Parçalama arızası (çekirdek döküldü)
[alperen@LAPTOP-EDN1J28M distsetup]$ gdb ./setup 
GNU gdb (GDB) Fedora Linux 13.2-2.fc38
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

--Type <RET> for more, q to quit, c to continue without paging--
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./setup...

This GDB supports auto-downloading debuginfo from the following URLs:
  <https://debuginfod.fedoraproject.org/>
Enable debuginfod for this session? (y or [n]) 
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
(No debugging symbols found in ./piluxsetup)
(gdb) run
Starting program: /home/alperen/Projeler/distsetup/piluxsetup 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
--- Welcome to installer ---
Language: 

Program received signal SIGSEGV, Segmentation fault.
__strcpy_evex () at ../sysdeps/x86_64/multiarch/strcpy-evex.S:223
223             vmovq   %VMM_128(0), (%rdi)
(gdb) 
数组 c struct malloc

评论

0赞 pmacfarlane 7/15/2023
如何定义结构?Language
1赞 Oka 7/15/2023
char *line;和。您必须以一种或另一种方式分配内存才能填充数据。现在,您正在从文件中读取字符并将它们写入内存中的有效随机位置。fgets (line, 80, supported_languages)fgets
0赞 Oka 7/15/2023
return tokenizer;后跟 - 就地标记数据。请注意不要返回指向本地缓冲区的指针,请注意,从函数返回后无法执行函数代码,因此文件句柄将被泄露。fclose(supported_languages);strtok
0赞 Oka 7/15/2023
如果您想要一个完整的答案,请提供一个最小的、可重复的示例,可以对其进行编译,否则我们只能真正提供零碎的评论。
0赞 Oka 7/15/2023
realloc (languages, line_count);- 第二个参数是您想要的内存大小(以字节为单位)。没有被调用的记录,因此您必须再次将其包含在计算中(即 )。reallocmallocsizeof (Language)realloc(languages, sizeof *languages * line_count)

答:

0赞 Chris 7/15/2023 #1

如评论中所述,段错误的最可能来源如下:

  char *line;
  while (fgets (line, 80, supported_languages) != NULL)
  {

在此代码中,是指向 的指针,但它未初始化。它不指向任何有效的内存。现在,它可能偶然指向允许它有时“工作”的记忆,但这不应该指望。像这样的代码“工作”是它能做的最危险的事情。linechar

相反,您需要为该读取分配内存。您可以在堆栈或堆上执行此操作。

char line[80];

艺术

char *line = malloc(80);

由于返回了指向 (via) 的指针,因此需要在作用域内存在,因此应选择使用 的动态分配。但是,以后无法释放它,因为它本身就变成了一个悬空的指针。linestrtokmallocline

您可以改为读入堆栈分配的 char 数组,然后将实际所需的字符串复制到适当大小的动态分配的 char 数组中,该数组可以返回。这将使您以后正确清理内存。fgets

0赞 Alperen İsa 7/15/2023 #2

我将这段代码添加到第 131 行。

    languages[line_count - 1].name = malloc(strlen(lang_name) + 1);
    languages[line_count - 1].code = malloc(strlen(lang_code) + 1);

该函数似乎没有将字符串复制到正确的地址。因此,我从内存中分配了临时空间,并将 和复制到空间中。之后,我只是从函数返回结构。但是,我必须使用此功能清除临时空间:strcpy ()lang_codelang_name

void _free_available_languages (Language* language_list,
                                int len_language_list)
{
  int i = 0;
  for (; i < len_language_list; i++)
  {
    free(language_list[i].name);
    free(language_list[i].code);
  }
  free (language_list);
}