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

bttbmeg0  于 2023-08-03  发布在  其他
关注(0)|答案(2)|浏览(136)

我有一个名为languages. c的源文件。我想读取get_availabile_languages()的data/languages.txt文本,并创建一个Language结构数组,并在每个元素中定义它们。
但是,当我尝试这样做时,我得到一个SIGSEGV错误。我创建的代码旨在完成以下任务。
1.开始数行数。在languages中创建一个元素,每行都有一个Language结构。
1.使用strtok ()=符号处拆分data/languages.txt中的字符串。将第一个赋值给lang_code变量,将第二个赋值给lang_name变量。
1.完成后,返回结构数组。

/* 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;
}

x

/* 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)

ohfgkhjo

ohfgkhjo1#

如评论中所述,segfault最有可能的来源如下:

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

字符串
在这段代码中,line是一个指向char的指针,但它没有初始化。它不指向任何有效内存。现在,它可能偶然指向允许它有时“工作”的内存,但这不应该被指望。像这样的代码“工作”是它能做的最危险的事情。
相反,您需要为该读取分配内存。你可以在栈或堆上做这个。

char line[80];


或者:

char *line = malloc(80);


由于返回了一个指向line的指针(通过strtok),因此需要在作用域中生存,因此应该选择使用malloc进行动态分配。但是,这不能在以后被释放,因为line本身变成了一个悬空指针。
您可以使用fgets读取堆栈分配的字符数组,然后将实际所需的字符串复制到可以返回的适当大小的动态分配的字符数组中。这会让你以后正确地清理你的记忆。

f0brbegy

f0brbegy2#

我将这段代码添加到第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);
}

相关问题