LoginSignup
0
0

More than 5 years have passed since last update.

オフラインリアルタイムどう書く E29 の実装例( C99)

Last updated at Posted at 2018-12-22

問題の名前 : アンエスケープ
問題 : http://nabetani.sakura.ne.jp/hena/orde29unes/
実装リンク集 : https://qiita.com/Nabetani/items/f2db9b916c0a301b744f

次回のイベントは 2月2日
see https://yhpg.doorkeeper.jp/events/84247

で。
C言語アドベントカレンダーに空きがあったので、C99で書いてみた。

C99
// clang -Wall -std=c99 e29.c
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum mode { normal, dq, sq, slash };

// strdup は標準じゃないので定義する
char *dup(char const *src) {
  size_t len = strlen(src) + 1;
  char *p = malloc(len);
  memcpy(p, src, len);
  return p;
}

typedef struct entry {
  char *p;
} entry;

typedef struct entries {
  entry *p;
  size_t size;
} entries;

void entries_add_empty_entry(entries *ents) {
  ++ents->size;
  if (ents->p) {
    ents->p = realloc(ents->p, sizeof(entry *) * ents->size);
  } else {
    ents->p = malloc(sizeof(entry *));
  }
  ents->p[ents->size - 1].p = dup("");
}

void entries_free(entries ents) {
  for (size_t i = 0; i < ents.size; ++i) {
    free(ents.p[i].p);
  }
  if (ents.p) {
    free(ents.p);
  }
}

void entries_append_char(entries *ents, char ch) {
  entry *e = &ents->p[ents->size - 1];
  size_t len = strlen(e->p);
  e->p = realloc(e->p, len + 2);
  e->p[len] = ch;
  e->p[len + 1] = 0;
}

enum mode do_normal(entries *ents, char ch) {
  switch (ch) {
  case '/':
    return slash;
  case '\'':
    return sq;
  case '"':
    return dq;
  default:
    entries_append_char(ents, ch);
    return normal;
  }
}
enum mode do_slash(entries *ents, char ch) {
  switch (ch) {
  case '/':
    entries_append_char(ents, ch);
    return normal;
  case '\'':
    entries_add_empty_entry(ents);
    return sq;
  case '"':
    entries_add_empty_entry(ents);
    return dq;
  default:
    entries_add_empty_entry(ents);
    entries_append_char(ents, ch);
    return normal;
  }
}

enum mode do_quote(entries *ents, char q, enum mode mode, char ch) {
  if (ch == q) {
    return normal;
  } else {
    entries_append_char(ents, ch);
    return mode;
  }
}

entries unescape(char const *src) {
  entries ents = {0, 0};
  entries_add_empty_entry(&ents);
  enum mode mode = normal;
  for (char const *p = src; *p; ++p) {
    switch (mode) {
    case normal:
      mode = do_normal(&ents, *p);
      break;
    case slash:
      mode = do_slash(&ents, *p);
      break;
    case dq:
      mode = do_quote(&ents, '"', mode, *p);
      break;
    case sq:
      mode = do_quote(&ents, '\'', mode, *p);
      break;
    default:
      fputs("logic error", stderr);
      exit(1);
    }
  }
  if (mode == normal) {
    return ents;
  } else {
    entries_free(ents);
    entries empty_ents = {0, 0};
    return empty_ents;
  }
}

// caller should free return value memory.
char const *solve(char const *src) {
  entries ents = unescape(src);
  size_t len = ents.size; // コンマの数と null terminator
  for (size_t i = 0; i < ents.size; ++i) {
    len += strlen(ents.p[i].p);
  }
  char *str = (char *)calloc(len + 1, 1);
  bool okay = 0<ents.size;
  for (size_t i = 0; okay && i < ents.size; ++i) {
    if (i != 0) {
      strcat(str, ",");
    }
    if (0 == *ents.p[i].p) {
      okay = false;
    }
    strcat(str, ents.p[i].p);
  }
  entries_free(ents);
  if (okay) {
    return str;
  } else {
    free(str);
    return dup("-");
  }
}

struct result {
  int success;
  int testcount;
};

void test_(struct result *r, char const *src, char const *expected) {
  char const *actual = solve(src);
  int okay = 0 == strcmp(actual, expected);
  if (okay) {
    ++r->success;
  }
  ++r->testcount;
  printf("%s : %s->%s(%s)\n", (okay ? "ok" : "**NG**"), src, actual, expected);
  free((void *)actual);
}

int main(void) {
  struct result r = {0};
#define test(src, expected) test_(&r, src, expected)
  /*0*/ test("foo/bar/baz", "foo,bar,baz");
  /*1*/ test("/foo/bar/baz'/", "-");
  /*2*/ test("\"", "-");
  // 中略
  /*63*/ test("Foo/Bar/\"Hoge'/'Fuga\"", "Foo,Bar,Hoge'/'Fuga");
#undef test
  printf("%d / %d\n", r.success, r.testcount);
  return r.testcount == r.success ? 0 : 1;
}

文字列処理が中心の問題なので、C言語は割と辛かった。
メモリリークしていないかどうか、ちょっと自信がない。

realloc しまくっているし、strcatstrlen でなめまくっているので、だいぶ遅いと思う。
しかしなんで strdup は標準じゃないんだろう。便利なんだけどなぁ。
realloc_cat みたいな関数もあったっていいと思う。

せっかくなので、C11 を名乗って strcpy_s を使おうと思ったんだけど、手元の clang も gcc-8 もそんな関数ないよというエラーになった。そういうものか。

0
0
0

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
0
0