LoginSignup
3
2

More than 1 year has passed since last update.

【C言語】ファイルディスクリプタからファイルの内容を読む

Last updated at Posted at 2022-01-04

:muscle:注意

これはある課題の1つなので、課題の題意の意図に沿って実装します

:muscle:概要1

どういったことをするかをまず書く

1)ファイルディスクリプタを受け取る
2)そのファイルからBUFFER_SIZE分読み込む
3)改行までをリターンする

ただし、ここでは以下の関数のみが使える

read, malloc, free

コンパイルオプションは以下

$ gcc -Wall -Wextra -Werror -D BUFFER_SIZE="任意の値" get_next_line.c get_next_line_utils.c

つまり、こんな感じのmain.cを作ってテストファイルの内容を出力できればOKってこと

main.c
#include <stdio.h>
#include <fcntl.h>
#include "get_next_line.h"

int	main(void)
{
	int		fd;
	char	*line;

	line = "";
	fd = open("test.txt", O_RDONLY);
	while (line)
	{
		line = get_next_line(fd);
		printf("> %s", line);
		free(line);
	}
	return (0);
}

:muscle:基本的な関数

get_next_line_utils.cに基本的な関数を書いていく
stdlib.hに入っているような関数は自作です
内容については触れません

get_next_line_utils.c
#include "get_next_line.h"

size_t	ft_strlen(const char *s)
{
	size_t	return_len;

	return_len = 0;
	while (s[return_len] != '\0')
		return_len ++;
	return (return_len);
}

char	*ft_strchr(char *s, int c)
{
	int	i;

	i = 0;
	if (!s)
		return (0);
	if (c == '\0')
		return ((char *)&s[ft_strlen(s)]);
	while (s[i] != '\0')
	{
		if (s[i] == (char) c)
			return ((char *)&s[i]);
		i++;
	}
	return (0);
}

char	*ft_strjoin(char *s1, char *s2)
{
	size_t	i;
	size_t	c;
	char	*rtn;

	if (!s1)
	{
		s1 = (char *)malloc(1 * sizeof(char));
		s1[0] = '\0';
	}
	if (!s1 || !s2)
		return (NULL);
	rtn = malloc((ft_strlen(s1) + ft_strlen(s2) + 1) * sizeof(char));
	if (rtn == NULL)
		return (NULL);
	i = -1;
	c = 0;
	if (s1)
		while (s1[++i] != '\0')
			rtn[i] = s1[i];
	while (s2[c] != '\0')
		rtn[i++] = s2[c++];
	rtn[ft_strlen(s1) + ft_strlen(s2)] = '\0';
	free(s1);
	return (rtn);
}

:muscle:心臓部分の実装

こんな感じで形を作ります。
何回も呼び出されるのでstatic charで保存する部分を定義します

get_next_line.c
/*
lineを定義(CHAR)
saveを定義(STATIC CHAR)

もし、fdが0より小さいもしくはBUFFER_SIZEが0以下の場合
	0を返す
saveに読み込んだファイルの内容を格納
もし、saveがなかった場合
	NULLを返す
lineに改行が来るまでを入れる
saveに改行後を入れる
lineをリターンする
*/

#include "get_next_line.h"

char	*get_next_line(int fd)
{
	char		*line;
	static char	*save;

	if (fd < 0 || BUFFER_SIZE <= 0)
		return (0);
	save = ft_read(fd, save);
	if (!save)
		return (NULL);
	line = ft_get_line(save);
	save = ft_save(save);
	return (line);
}

サブ関数を作っていきます。
まず、saveに'\n'が来るまでファイルを読み込み、代入する関数を作ります

get_next_line.c
/*
tmpを定義(CHAR)
read関数の返り値を格納するread_rtnを定義(INT)

tmpの領域をBUFFER_SIZE+1分、charのサイズで確保する
もし、tmpがNULLの場合
	NULLを返す(mallocが失敗したときのため)
read_rtn=1で初期化
saveに'\n'が入っていない、かつread_rtnが0でない場合
	BUFFER_SIZE分、ファイルを読み、tmpに格納する
	もし、read_rtnが-1の場合(readに失敗した場合)
		tmpをフリーする
		NULLを返す
	tmpをNULL止めする
	saveにtmpをくっつけて再代入
tmpをフリーする
saveを返す
*/

char	*ft_read(int fd, char *save)
{
	char	*tmp;
	int		read_rtn;

	tmp = malloc((BUFFER_SIZE + 1) * sizeof(char));
	if (!tmp)
		return (NULL);
	read_rtn = 1;
	while (!ft_strchr(save, '\n') && read_rtn != 0)
	{
		read_rtn = read(fd, tmp, BUFFER_SIZE);
		if (read_rtn == -1)
		{
			free(tmp);
			return (NULL);
		}
		tmp[read_rtn] = '\0';
		save = ft_strjoin(save, tmp);
	}
	free(tmp);
	return (save);
}

saveの中から改行が来るまでを取り出して、返す関数を作ります

get_next_line.c
/*
汎用カウンターiを定義(INT)
'\n'の前までを保存する*rtnを定義(CHAR)

i=0で初期化
もし、saveが空な場合
	NULLを返す
save[i]に内容がある間、かつsave[i]が'\n'でない場合
	iに1ずつ足していく('\n'を含まずに文字数をカウントしている)
マロックでrtnの領域を確保する('\n'と'\0'の場所のため+2している)
もし、rtnがない場合
	NULLを返す(mallocが失敗したときのため)
i=0で初期化
save[i]に内容がある間、かつsave[i]が'\n'でない場合
	rtn[i]にsave[i]を代入する
	iに1を足す
もし、save[i]に'\n'が来ていた場合
	rtn[i]に'\n'を代入する
rtn[i]をNULL止めする
rtnを返す
*/

char	*ft_get_line(char *save)
{
	int		i;
	char	*rtn;

	i = 0;
	if (!save[i])
		return (NULL);
	while (save[i] && save[i] != '\n')
		i++;
	rtn = (char *)malloc(sizeof(char) * (i + 2));
	if (!rtn)
		return (NULL);
	i = 0;
	while (save[i] && save[i] != '\n')
	{
		rtn[i] = save[i];
		i++;
	}
	if (save[i] == '\n')
	{
		rtn[i] = '\n';
		i++;
	}
	rtn[i] = '\0';
	return (rtn);
}

saveの中から改行の後を取り出して、返す関数を作ります

get_next_line.c
/*
汎用カウンターiを定義(INT)
汎用カウンターcを定義(INT)
'\n'の後ろを保存する*sを定義(CHAR)

i=0で初期化
save[i]があり、かつsave[i]が'\n'でない場合、
	iに1を足し続ける
もし、save[i]がない場合、('\n'の後ろに何もない場合)
	saveをフリーする
	NULLを返す
sを「saveの長さ - '\n'までの長さ + 1」で確保する('\n'の後ろとNULL分確保)
もし、sがNULLの場合
	NULLを返す(mallocが失敗したときのため)
iに1を足す
c = 0で初期化
save[i]がNULL出ない場合
	s[c++] = save[i]で'\n'以降を代入し続ける
sをNULL止めする
saveをフリーする
sを返す
*/

char	*ft_save(char *save)
{
	int		i;
	int		c;
	char	*s;

	i = 0;
	while (save[i] && save[i] != '\n')
		i++;
	if (!save[i])
	{
		free(save);
		return (NULL);
	}
	s = (char *)malloc((ft_strlen(save) - i + 1) * sizeof(char));
	if (!s)
		return (NULL);
	i++;
	c = 0;
	while (save[i])
		s[c++] = save[i++];
	s[c] = '\0';
	free(save);
	return (s);
}

:muscle:検証

$ cat test.txt -e
hello world$
hello$
$
 $
  $
   $
   $
   $
    $
     $
      $
      $
       a$
       g$
       a$
       gr@[rgeapage,pg@agre$
       ae$
       gpamtamt$
       $
       $
       $
       $
       $
aopt,,,,,gbg$
$ clear && gcc -Wall -Wextra -Werror -D BUFFER_SIZE=42 get_next_line.c get_next_line_utils.c main.c -g && ./a.out | cat -e
> hello world$
> hello$
> $
>  $
>   $
>    $
>    $
>    $
>     $
>      $
>       $
>       $
>        a$
>        g$
>        a$
>        gr@[rgeapage,pg@agre$
>        ae$
> (null)$ 

でけた!
詳しい説明とかできてないけど、もしかしたら書き加えるかも

:muscle:参考サイト

3
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2