본문 바로가기

Develop/Flutter

[Flutter] 문자열 명명 형식 변환기 (String Case Converter)

1. 개요

소프트웨어를 개발할 때 문자열의 명명 형식(String Naming Type)을 변환해야 할 경우가 왕왕 있었는데, 이에 따라 자유자재로 변환이 가능한 Utilty 기능의 필요성을 느껴 개발하였고 이 포스팅에서 소개하고자 한다.

1.1. 명명 형식 종류

명명 형식 예시
Lower Snake Case hello_world
Upper Snake Case HELLO_WORLD
Lower Skewer Case hello-world
Upper Skewer Case HELLO-WORLD
Pascal Case HelloWorld
Camel Case helloWorld
Title Case Hello World
Lower Case hello world
Upper Case HELLO WORLD

2. 활용 문법

2.1. extension

2.2. get

3. 구현

3.1. 열거형 (Enum)

명명 형식의 종류를 나타내는 열거형 StringCaseType 이다.

enum StringCaseType {
  lowerSnakeCase, upperSnakeCase,
  lowerSkewerCase, upperSkewerCase,
  pascalCase, camelCase,
  titleCase, lowerCase, upperCase;
}

3.2. 클래스 (Class)

3.2.1. 생성자 (Constructor)

전달받은 value 매개변수가 어떤 종류인지 파악하여 해당 종류에 대한 Setter 를 사용해 _value 값을 최신화한다.

StringCaseConverter(String value) {
  var type = getCaseType(value);
  switch (type) {
    case StringCaseType.lowerSnakeCase: lowerSnakeCase = value; break;
    case StringCaseType.upperSnakeCase: upperSnakeCase = value; break;
    case StringCaseType.lowerSkewerCase: lowerSkewerCase = value; break;
    case StringCaseType.upperSkewerCase: upperSkewerCase = value; break;
    case StringCaseType.pascalCase: pascalCase = value; break;
    case StringCaseType.camelCase: camelCase = value; break;
    case StringCaseType.titleCase: titleCase = value; break;
    case StringCaseType.lowerCase: lowerCase = value; break;
    case StringCaseType.upperCase: upperCase = value; break;
    default: throw ArgumentError('Unknown case type for: $value');
  }
}

3.2.2. _value 속성

StringCaseConverter 클래스가 가지는 유일한 속성으로 Lower Snake Case 형식으로 저장된다.

late String _value;

3.2.3. 접근자 (Accessor), 설정자 (Mutator)

/// lower_snake_case
String get lowerSnakeCase => _value;
set lowerSnakeCase(String s) => _value = s;

/// UPPER_SNAKE_CASE
String get upperSnakeCase => _value.toUpperCase();
set upperSnakeCase(String s) => _value = s.toLowerCase();

/// lower_skewer_case
String get lowerSkewerCase => _value.replaceAll('_', '-');
set lowerSkewerCase(String s) => _value = s.replaceAll('-', '_');

/// UPPER_SKEWER_CASE
String get upperSkewerCase => lowerSkewerCase.toUpperCase();
set upperSkewerCase(String s) => lowerSkewerCase = s.toLowerCase();

/// PascalCase
String get pascalCase {
  final words = _splitToWords(_value);
  return words.map(_capitalizeFirstLetter).join();
}
set pascalCase(String s) {
  final words = _splitToWords(s);
  _value = words.map((word) => word.toLowerCase()).join('_');
}

/// camelCase
String get camelCase {
  final words = _splitToWords(_value);
  return [words.first.toLowerCase(), ...words.skip(1).map(_capitalizeFirstLetter)].join();
}
set camelCase(String s) {
  final words = _splitToWords(s);
  _value = words.map((word) => word.toLowerCase()).join('_');
}

/// Title Case
String get titleCase {
  final words = _splitToWords(_value.replaceAll('_', ' ').replaceAll('-', ' '));
  return words.map(_capitalizeFirstLetter).join(' ');
}
set titleCase(String s) {
  final words = s.split(RegExp(r'\s+'));
  _value = words.map((word) => word.toLowerCase()).join('_');
}

/// lower case
String get lowerCase => titleCase.toLowerCase();
set lowerCase(String s) => titleCase = s.toLowerCase();

/// UPPER CASE
String get upperCase => titleCase.toUpperCase();
set upperCase(String s) => titleCase = s.toUpperCase();

3.2.4. 정적 메소드 (Static Method)

3.2.4.1. getCaseType()

문자열 str 의 명명 형식을 반환한다.

  • 매개변수
자료형 변수명 설명
String str 명명 형식을 조사할 문자열
  • 반환값
자료형 설명
StringCaseType str 의 명명 형식
static StringCaseType getCaseType(String str) {
  var isLower = str == str.toLowerCase();
  var isUpper = str == str.toUpperCase();
  var hasUnderscore = str.contains('_');
  var hasHyphen = str.contains('-');
  var hasSpace = str.contains(' ');

  /// 모든 문자가 소문자인 경우
  if (isLower) {
    if (hasUnderscore) return StringCaseType.lowerSnakeCase;
    if (hasHyphen) return StringCaseType.lowerSkewerCase;
    return StringCaseType.lowerCase;
  }

  /// 모든 문자가 대문자인 경우
  else if (isUpper) {
    if (hasUnderscore) return StringCaseType.upperSnakeCase;
    if (hasHyphen) return StringCaseType.upperSkewerCase;
    return StringCaseType.upperCase;
  }

  /// 소문자와 대문자가 섞인 경우
  else {
    var firstChar = str[0];
    var isFirstCharUpper = firstChar.toUpperCase() == firstChar;
    var isFirstCharLower = firstChar.toLowerCase() == firstChar;
    if (hasSpace) return StringCaseType.titleCase;
    if (isFirstCharUpper && !hasUnderscore && !hasHyphen) return StringCaseType.pascalCase;
    if (isFirstCharLower && !hasUnderscore && !hasHyphen) return StringCaseType.camelCase;
  }

  throw ArgumentError('Unknown case type for: $str');
}

3.2.4.2. _splitToWords()

문자열 str- 또는 _ 를 구분자로 하여 단어별로 구분한 배열을 반환한다.

  • 매개변수
자료형 변수명 설명
String str 단어별로 구분할 문자열
  • 반환값
자료형 설명
List<String> str 를 단어별로 구분한 배열
static List<String> _splitToWords(String str) {
  final pattern = RegExp(r'[A-Z]?[a-z]+|[A-Z]+(?=[A-Z][a-z]|$)|\d+');
  final matches = pattern.allMatches(str.replaceAll(RegExp(r'[-_]'), ' '));
  return matches.map((m) => m.group(0) ?? '').toList();
}

3.2.4.3. _capitalizeFirstLetter()

문자열 str 의 첮 문자를 대문자로 바꾼다.

  • 매개변수
자료형 변수명 설명
String str 첫 문자를 대문자로 바꿀 문자열
  • 반환값
자료형 설명
String str 의 첫 문자를 대문자로 바꾼 문자열
static String _capitalizeFirstLetter(String str) {
  if (str.isEmpty) return str;
  return str[0].toUpperCase() + str.substring(1).toLowerCase();
}

4. 전체 코드

enum StringCaseType {
  lowerSnakeCase, upperSnakeCase,
  lowerSkewerCase, upperSkewerCase,
  pascalCase, camelCase,
  titleCase, lowerCase, upperCase;
}

class StringCaseConverter {
  StringCaseConverter(String value) {
    var type = getCaseType(value);
    switch (type) {
      case StringCaseType.lowerSnakeCase: lowerSnakeCase = value; break;
      case StringCaseType.upperSnakeCase: upperSnakeCase = value; break;
      case StringCaseType.lowerSkewerCase: lowerSkewerCase = value; break;
      case StringCaseType.upperSkewerCase: upperSkewerCase = value; break;
      case StringCaseType.pascalCase: pascalCase = value; break;
      case StringCaseType.camelCase: camelCase = value; break;
      case StringCaseType.titleCase: titleCase = value; break;
      case StringCaseType.lowerCase: lowerCase = value; break;
      case StringCaseType.upperCase: upperCase = value; break;
      default: throw ArgumentError('Unknown case type for: $value');
    }
  }

  /// default: lower_snake_case
  late String _value;

  /// lower_snake_case
  String get lowerSnakeCase => _value;
  set lowerSnakeCase(String s) => _value = s;

  /// UPPER_SNAKE_CASE
  String get upperSnakeCase => _value.toUpperCase();
  set upperSnakeCase(String s) => _value = s.toLowerCase();

  /// lower_skewer_case
  String get lowerSkewerCase => _value.replaceAll('_', '-');
  set lowerSkewerCase(String s) => _value = s.replaceAll('-', '_');

  /// UPPER_SKEWER_CASE
  String get upperSkewerCase => lowerSkewerCase.toUpperCase();
  set upperSkewerCase(String s) => lowerSkewerCase = s.toLowerCase();

  /// PascalCase
  String get pascalCase {
    final words = _splitToWords(_value);
    return words.map(_capitalizeFirstLetter).join();
  }
  set pascalCase(String s) {
    final words = _splitToWords(s);
    _value = words.map((word) => word.toLowerCase()).join('_');
  }

  /// camelCase
  String get camelCase {
    final words = _splitToWords(_value);
    return [words.first.toLowerCase(), ...words.skip(1).map(_capitalizeFirstLetter)].join();
  }
  set camelCase(String s) {
    final words = _splitToWords(s);
    _value = words.map((word) => word.toLowerCase()).join('_');
  }

  /// Title Case
  String get titleCase {
    final words = _splitToWords(_value.replaceAll('_', ' ').replaceAll('-', ' '));
    return words.map(_capitalizeFirstLetter).join(' ');
  }
  set titleCase(String s) {
    final words = s.split(RegExp(r'\s+'));
    _value = words.map((word) => word.toLowerCase()).join('_');
  }

  /// lower case
  String get lowerCase => titleCase.toLowerCase();
  set lowerCase(String s) => titleCase = s.toLowerCase();

  /// UPPER CASE
  String get upperCase => titleCase.toUpperCase();
  set upperCase(String s) => titleCase = s.toUpperCase();

  static StringCaseType getCaseType(String str) {
    var isLower = str == str.toLowerCase();
    var isUpper = str == str.toUpperCase();
    var hasUnderscore = str.contains('_');
    var hasHyphen = str.contains('-');
    var hasSpace = str.contains(' ');

    /// 모든 문자가 소문자인 경우
    if (isLower) {
      if (hasUnderscore) return StringCaseType.lowerSnakeCase;
      if (hasHyphen) return StringCaseType.lowerSkewerCase;
      return StringCaseType.lowerCase;
    }

    /// 모든 문자가 대문자인 경우
    else if (isUpper) {
      if (hasUnderscore) return StringCaseType.upperSnakeCase;
      if (hasHyphen) return StringCaseType.upperSkewerCase;
      return StringCaseType.upperCase;
    }

    /// 소문자와 대문자가 섞인 경우
    else {
      var firstChar = str[0];
      var isFirstCharUpper = firstChar.toUpperCase() == firstChar;
      var isFirstCharLower = firstChar.toLowerCase() == firstChar;
      if (hasSpace) return StringCaseType.titleCase;
      if (isFirstCharUpper && !hasUnderscore && !hasHyphen) return StringCaseType.pascalCase;
      if (isFirstCharLower && !hasUnderscore && !hasHyphen) return StringCaseType.camelCase;
    }

    throw ArgumentError('Unknown case type for: $str');
  }

  static List<String> _splitToWords(String str) {
    final pattern = RegExp(r'[A-Z]?[a-z]+|[A-Z]+(?=[A-Z][a-z]|$)|\d+');
    final matches = pattern.allMatches(str.replaceAll(RegExp(r'[-_]'), ' '));
    return matches.map((m) => m.group(0) ?? '').toList();
  }

  static String _capitalizeFirstLetter(String str) {
    if (str.isEmpty) return str;
    return str[0].toUpperCase() + str.substring(1).toLowerCase();
  }
}

5. 사용법

void main() {
  print(StringCaseConverter('hello_world').pascalCase);      // HelloWorld
  print(StringCaseConverter('HELLO_WORLD').pascalCase);      // HelloWorld
  print(StringCaseConverter('hello-world').pascalCase);      // HelloWorld
  print(StringCaseConverter('HELLO-WORLD').pascalCase);      // HelloWorld
  print(StringCaseConverter('HelloWorld').pascalCase);       // HelloWorld
  print(StringCaseConverter('helloWorld').pascalCase);       // HelloWorld
  print(StringCaseConverter('Hello World').pascalCase);      // HelloWorld
  print(StringCaseConverter('hello world').pascalCase);      // HelloWorld
  print(StringCaseConverter('HELLO WORLD').pascalCase);      // HelloWorld

  print(StringCaseConverter('hello_world').lowerSnakeCase);  // hello_world
  print(StringCaseConverter('hello_world').upperSnakeCase);  // HELLO_WORLD
  print(StringCaseConverter('hello_world').lowerSkewerCase); // hello-world
  print(StringCaseConverter('hello_world').upperSkewerCase); // HELLO-WORLD
  print(StringCaseConverter('hello_world').pascalCase);      // HelloWorld
  print(StringCaseConverter('hello_world').camelCase);       // helloWorld
  print(StringCaseConverter('hello_world').titleCase);       // Hello World
  print(StringCaseConverter('hello_world').lowerCase);       // hello world
  print(StringCaseConverter('hello_world').upperCase);       // HELLO WORLD
}

관련 링크

728x90