C言語 fileから1行づつ読み取る関数

3 min read読了の目安(約2700字

要件

  • 使用可能関数
    • read, malloc, free

全体を通して

1つの関数に1つの機能を意識して書いた。
処理の流れが以下の3つ。get_next_lineを見ればどのような流れになっているのか、わかるようにした。

  1. 入力値のエラーチェック
  2. fileの読み取り
  3. 読み取ったfileの情報を加工して、格納する。
状態 返り値
EOFに到達し、終了 0
一行読み取り終了 1
エラー -1

コード

#include "get_next_line.h"

#ifndef BUFFER_SIZE
# define BUFFER_SIZE 1024
#endif

#define SAFE_FREE(p) {free(p); p = NULL;}
#define ERROR -1
#define CONTINUE 1
#define	END 0

static size_t	ft_strlen(char *s)
{
	size_t i = 0;

	if (!s)
		return (0);
	while (s[i])
		i++;
	return (i);
}

static char	*free_return(char **s, char *p)
{
	SAFE_FREE(*s);
	return (p);
}

static char	*ft_strjoin(char *s, char *d)
{
	char *dst;
	char *head;

	if (!(dst = malloc(ft_strlen(s) + ft_strlen(d) + 1)))
		return (NULL);
	head = dst;
	if (s)
		while (*s)
			*dst++ = *s++;
	if (d)
		while (*d)
			*dst++ = *d++;
	*dst = '\0';
	return (head);
}

static char	*ft_strn(char *s)
{
	if (!s)
		return (0);
	while (*s && *s != '\n')
		s++;
	return (s);
}

static int	is_n(char *s)
{
	if (!s)
		return (0);
	while (*s && *s != '\n')
		s++;
	return (*s == '\n');
}

static int	store_line(char **line, char **s)
{
	char *tmp;
	char *dst;

	tmp = *s;
	if (!(dst = malloc(ft_strn(tmp) - tmp + 1)))
	{
		SAFE_FREE(*s);
		return (ERROR);
	}
	*line = dst;
	while (*tmp && *tmp != '\n')
		*dst++ = *tmp++;
	*dst = '\0';
	if (*tmp == '\0')
	{
		SAFE_FREE(*s);
		return (END);
	}
	else
		*s = free_return(s, ft_strjoin(++tmp, NULL));
	return (CONTINUE);
}

static int	read_file(int fd, char **s)
{
	char	*buf;
	ssize_t	rc;

	if (!(buf = malloc((size_t)BUFFER_SIZE + 1)))
		return (ERROR);
	rc = 1;
	while (0 < rc && !is_n(*s))
	{
		if ((rc = read(fd, buf, BUFFER_SIZE)) == ERROR)
		{
			SAFE_FREE(buf);
			SAFE_FREE(*s);
			return (ERROR);
		}
		buf[rc] = '\0';
		if (!(*s = free_return(s, ft_strjoin(*s, buf))))
			return (ERROR);
	}
	SAFE_FREE(buf);
	if (rc == 0)
		return (END);
	return (CONTINUE);
}

int	get_next_line(int fd, char **line)
{
	static char	*s;

	if (!line || BUFFER_SIZE <= 0 || read(fd, 0, 0) == ERROR)
		return (ERROR);
	if (read_file(fd, &s) == ERROR)
		return (ERROR);
	return (store_line(line, &s));
}

read_file関数

改行か、fileの終端がくるまで、readを回し続ける関数。
BUFFER_SIZEをコンパイル時に設定することで、readの呼び出し回数を調整できるようになっている。

store_line関数

read_file関数で読み取ったデータを、加工し、static変数、line引数に格納する。

free_return関数

freeをするために、tmpを使って値の入れ替えを行う。というコードの書き方をしていたが、変数を減らし、可読性を上げる意図で、導入した。


char	*free_return(char **s, char *p)
{
	SAFE_FREE(*s);
	return (p);
}

ft_strn、is_n関数

1つにまとめたかったが、できなかった。

char	*ft_strn(char *s)
{
	if (!s)
		return (0);
	while (*s && *s != '\n')
		s++;
	return (s);
}

int	is_n(char *s)
{
	if (!s)
		return (0);
	while (*s && *s != '\n')
		s++;
	return (*s == '\n');
}