📁

ファイルディスクリプタを読み込んで、1行ずつ出力させる

2022/10/08に公開

42Tokyoの課題を解き終わったので、備忘録として。

解釈の違いや理解が足りていない部分があるかと思いますので、ご指摘いただけると助かります。あと、参考程度にしてください。

また、課題に取り組んでる方は結構なネタバレ(?)答え(?)になると思います…多分(?)


42Tokyoとは?

42 Tokyo(フォーティーツー)は、フランス発のエンジニア養成機関です。
アメリカ・シリコンバレーをはじめ、世界各国に教育を展開しています。
完全無料で、革新的なカリキュラムを学習可能。https://42tokyo.jp/

課題について

今回の課題は、ファイルディスクリプタと任意のBUFFER_SIZEを渡し、改行までの一文を出力させるというもの。
実際のコードはこちらから。
https://github.com/misato230/get_next_line.git

この課題のポイント

  • メモリの操作
    malloc→freeがしっかりとできているか、無駄なメモリを確保していないかを意識する
    BUFFER_SIZEが毎回変わるため、1など小さい数字から10000程度の大きいBUFFER_SIZEなどマルチに対応できる柔軟性を実装しなければいけない

  • static 変数(静的変数)の扱い
    Static 変数とは、プログラム中で使用する変数のうち、プログラムの開始から終了まで値が保持され続けるもの。
    この課題でStatic変数を使う理由として、一度に改行以上にファイルディスクリプタから読み込む場合があるため、何度も同じ部分を読み込まないように使用する。(プログラム終了まで値を確保させておく)

課題の制約

  • 使える関数
    read, malloc, free と自作関数5つまで
  • コンパイルするときのオプション
    -D BUFFER_SIZE=XX
  • 期待される戻り値
    改行までの1行を出力する
    エラーがあった場合は、NULLで返す

課題を解く順番

1.ファイルディスクリプタから、read関数を使ってBUFFER_SIZE分読み込む
2.読み込んだ中に改行が含まれていなかったら、改行が来るまで1を行う
3.改行までの部分を抜き出す
4.改行後の部分をstatic変数に上書きする


実際の実装コード

1,2 の部分

char	*read_and_joint(int fd, char *memo)
{
	char	*tmp;
	char	*buffer;
	ssize_t	rd_len;

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

3の部分

char	*put_return(char *memo)
{
	size_t	i;
	char	*str;

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

4の部分

char	*save_next(char *memo)
{
	size_t	i;
	char	*tmp;

	i = 0;
	while (memo[i] != '\0' && memo[i] != '\n')
		i++;
	if (!memo[i])
	{
		free(memo);
		return (NULL);
	}
	i++;
	tmp = strdup(&memo[i]);
	free(memo);
	if (!tmp)
		return (NULL);
	memo = tmp;
	return (memo);
}

追加した自作関数について

二つの文字列を結合させる関数を自作しました。

char	*ft_strjoin(char *s1, char *s2)
{
	size_t	i;
	size_t	j;
	char   *new_str;

	i = 0;
	j = 0;
	if (s1 == NULL || s2 == NULL)
		return (NULL);
	new_str = malloc(sizeof(char) * (strlen(s1) + strlen(s2) + 1));
	if (new_str == NULL)
		return (NULL);

	while (s1[i] != '\0')
	{
		new_str[i] = s1[i];
		i++;
	}
	while (s2[j] != '\0')
	{
		new_str[i] = s2[j];
		i++;
		j++;
	}
	new_str[i] = '\0';
	return (new_str);
}

レビューで指摘されたこと

  • BUFFER_SIZEをマクロで指定してもいい(しなくてもいい)
# define FD_MAX 256 (256の理由はguacamoleの横の幅…らしい)
# define BUF_MAX INT_MAX
# define BUFFER_SIZE 80(適当の数字で問題ないらしい…BUFFER_SIZEを入力しなかった時に適応してもらうため)
  • 標準入力にも対応させる
    fdの数値が0の場合は、標準入力となる
    コンパイルした後にキーボードから入力→同じ文字列が戻ってくる→ctrl + Dで戻る

最後に

この課題の意味、ファイルディスクリプタの意味が理解できるまで時間がかかりました。(今でも
100理解しているわけではないですが…)

C言語まだまだですね…がんばろ。

Discussion