Java에서 명령 행 인수 처리 : 대소 문자 마감

명령 줄에서 시작된 많은 Java 응용 프로그램은 인수를 사용하여 동작을 제어합니다. 이러한 인수는 응용 프로그램의 정적 main()메서드에 전달 된 문자열 배열 인수에서 사용할 수 있습니다 . 일반적으로 옵션 (또는 스위치) 및 실제 데이터 인수의 두 가지 유형의 인수가 있습니다. Java 애플리케이션은 이러한 인수를 처리하고 두 가지 기본 작업을 수행해야합니다.

  1. 사용 된 구문이 유효하고 지원되는지 확인
  2. 응용 프로그램이 작업을 수행하는 데 필요한 실제 데이터를 검색합니다.

종종 이러한 작업을 수행하는 코드는 각 응용 프로그램에 대해 맞춤 제작되므로 특히 요구 사항이 하나 또는 두 가지 옵션으로 단순한 경우를 넘어서는 경우 생성 및 유지 관리에 상당한 노력이 필요합니다. Options이 기사에서 설명 하는 클래스는 가장 복잡한 상황을 쉽게 처리 할 수있는 일반적인 접근 방식을 구현합니다. 이 클래스는 필수 옵션 및 데이터 인수에 대한 간단한 정의를 허용하고 철저한 구문 검사와 이러한 검사 결과에 대한 쉬운 액세스를 제공합니다. 제네릭 및 typesafe 열거와 같은 새로운 Java 5 기능도이 프로젝트에 사용되었습니다.

명령 줄 인수 유형

수년 동안, 나는 그들의 동작을 제어하기 위해 명령 줄 인수를 취하는 몇 가지 Java 도구를 작성했습니다. 초기에 다양한 옵션을 처리하기 위해 코드를 수동으로 생성하고 유지 관리하는 것이 성가신 일이었습니다. 이로 인해이 작업을 용이하게하는 프로토 타입 클래스가 개발되었지만, 면밀히 살펴보면 명령 줄 인수에 대해 가능한 다양한 종류의 수가 상당하다는 것이 판명 되었기 때문에 해당 클래스에는 한계가 있음이 인정되었습니다. 결국 저는이 문제에 대한 일반적인 해결책을 개발하기로 결정했습니다.

이 솔루션을 개발할 때 두 가지 주요 문제를 해결해야했습니다.

  1. 명령 줄 옵션이 발생할 수있는 모든 종류 식별
  2. 아직 개발되지 않은 클래스를 사용할 때 사용자가 이러한 다양성을 표현할 수있는 간단한 방법을 찾습니다.

문제 1을 분석 한 결과 다음과 같은 관찰이 이루어졌습니다.

  • 명령 줄 데이터 인수와 반대되는 명령 줄 옵션은 고유하게 식별하는 접두사로 시작합니다. 접두사 예에는 -Unix 플랫폼의 경우 대시 ( ) -a또는 /Windows 플랫폼의 경우 슬래시 ( ) 와 같은 옵션이 포함 됩니다.
  • 옵션은 단순 스위치 (즉, -a존재 여부)이거나 값을 가질 수 있습니다. 예 :

    java MyTool -a -b logfile.inp 
  • 값을받는 옵션은 실제 옵션 키와 값 사이에 다른 구분 기호를 가질 수 있습니다. 이러한 구분 기호는 공백, 콜론 ( :) 또는 등호 ( =) 일 수 있습니다.

    java MyTool -a -b logfile.inp java MyTool -a -b : logfile.inp java MyTool -a -b = logfile.inp 
  • 값을 취하는 옵션은 한 단계 더 복잡해질 수 있습니다. Java가 환경 속성 정의를 지원하는 방식을 예로 들어 보겠습니다.

    java -Djava.library.path = / usr / lib ... 
  • 따라서 실제 옵션 키 ( D), 구분 기호 ( =) 및 옵션의 실제 값 ( /usr/lib) 외에 추가 매개 변수 ( java.library.path)는 원하는 수의 값을 가질 수 있습니다 (위의 예에서는이 구문을 사용하여 다양한 환경 속성을 지정할 수 있습니다. ). 이 기사에서는이 매개 변수를 "detail"이라고합니다.
  • 옵션에는 또한 다중 속성이 있습니다. 필수 또는 선택 일 수 있으며 허용되는 횟수도 다를 수 있습니다 (예 : 정확히 한 번, 한 번 이상 또는 기타 가능성).
  • 데이터 인수는 모두 접두어로 시작하지 않는 명령 줄 인수입니다. 여기에서 이러한 데이터 인수의 허용 가능한 수는 최소 및 최대 수 사이에서 달라질 수 있습니다 (반드시 동일하지는 않음). 또한 일반적으로 응용 프로그램에서는 이러한 데이터 인수가 명령 줄에서 마지막에 있어야하지만 항상 그럴 필요는 없습니다. 예를 들면 :

    java MyTool -a -b = logfile.inp data1 data2 data3 // 끝의 모든 데이터 

    또는

    java MyTool -a data1 data2 -b = logfile.inp data3 // 응용 프로그램에 허용 될 수 있음 
  • 더 복잡한 애플리케이션은 둘 이상의 옵션 세트를 지원할 수 있습니다.

    java MyTool -a -b datafile.inp java MyTool -k [-verbose] foo bar duh java MyTool -check -verify logfile.out 
  • 마지막으로 애플리케이션은 알 수없는 옵션을 무시하도록 선택하거나 이러한 옵션을 오류로 간주 할 수 있습니다.

그래서 사용자가 이러한 모든 종류를 표현할 수있는 방법을 고안 할 때이 기사의 기초로 사용되는 다음과 같은 일반 옵션 양식을 고안했습니다.

[[]] 

이 형식은 위에서 설명한대로 다중성 속성과 결합되어야합니다.

위에서 설명한 옵션의 일반적인 형태의 제약 내 Options에서이 기사에서 설명 하는 클래스는 Java 애플리케이션이 가질 수있는 모든 명령 줄 처리 요구에 대한 일반적인 솔루션이되도록 설계되었습니다.

도우미 클래스

Options이 문서에서 설명하는 솔루션의 핵심 클래스 클래스는,이 개 도우미 클래스와 함께 제공 :

  1. OptionData:이 클래스는 하나의 특정 옵션에 대한 모든 정보를 보유합니다.
  2. OptionSet:이 클래스는 옵션 세트를 보유합니다. Options그 자체는 그러한 세트를 얼마든지 보유 할 수 있습니다.

이러한 클래스의 세부 사항을 설명하기 전에 클래스의 다른 중요한 개념을 Options소개해야합니다.

Typesafe 열거 형

접두사, 구분 기호 및 다중 속성은 Java 5에서 처음으로 제공되는 기능인 열거 형으로 캡처되었습니다.

공개 열거 형 접두사 {DASH ( '-'), SLASH ( '/'); 개인 문자 c; private Prefix (char c) {this.c = c; } char getName () {return c; }} public enum Separator {COLON ( ':'), EQUALS ( '='), BLANK ( ''), NONE ( 'D'); 개인 문자 c; private Separator (char c) {this.c = c; } char getName () {return c; }} public enum 다중성 {ONCE, ONCE_OR_MORE, ZERO_OR_ONE, ZERO_OR_MORE; }

열거 형을 사용하면 몇 가지 장점이 있습니다. 유형 안전성이 향상되고 허용되는 값 집합을 엄격하고 쉽게 제어 할 수 있습니다. 열거 형은 일반화 된 컬렉션에서도 편리하게 사용할 수 있습니다.

주의 PrefixSeparator열거 형은 실제의 정의를 허용, 자신의 생성자가 문자 를이 열거 인스턴스를합니다 (대 나타내는 이름을 특정 열거 인스턴스를 참조하는 데 사용). 이러한 문자는 이러한 열거 형의 getName()메서드를 사용하여 검색 할 수 있으며 문자는 java.util.regex패키지의 패턴 구문에 사용됩니다. 이 패키지는 Options클래스 에서 구문 검사 중 일부를 수행하는 데 사용되며 세부 사항은 다음과 같습니다.

Multiplicity열거는 현재 네 가지 값을 지원합니다 :

  1. ONCE:이 옵션은 정확히 한 번만 발생해야합니다.
  2. ONCE_OR_MORE: The option has to occur at least once
  3. ZERO_OR_ONCE: The option can either be absent or present exactly once
  4. ZERO_OR_MORE: The option can either be absent or present any number of times

More definitions can easily be added should the need arise.

The OptionData class

The OptionData class is basically a data container: firstly, for the data describing the option itself, and secondly, for the actual data found on the command line for that option. This design is already reflected in the constructor:

OptionData(Options.Prefix prefix, String key, boolean detail, Options.Separator separator, boolean value, Options.Multiplicity multiplicity) 

The key is used as the unique identifier for this option. Note that these arguments directly reflect the findings described earlier: a full option description must have at least a prefix, a key, and multiplicity. Options taking a value also have a separator and might accept details. Note also that this constructor has package access, so applications cannot directly use it. Class OptionSet's addOption() method adds the options. This design principle has the advantage that we have much better control on the actual possible combinations of arguments used to create OptionData instances. For example, if this constructor were public, you could create an instance with detail set to true and value set to false, which is of course nonsense. Rather than having elaborate checks in the constructor itself, I decided to provide a controlled set of addOption() methods.

The constructor also creates an instance of java.util.regex.Pattern, which is used for this option's pattern-matching process. One example would be the pattern for an option taking a value, no details, and a nonblank separator:

pattern = java.util.regex.Pattern.compile(prefix.getName() + key + separator.getName() + "(.+)$"); 

The OptionData class, as already mentioned, also holds the results of the checks performed by the Options class. It provides the following public methods to access these results:

int getResultCount() String getResultValue(int index) String getResultDetail(int index) 

The first method, getResultCount(), returns the number of times an option was found. This method design directly ties in with the multiplicity defined for the option. For options taking a value, this value can be retrieved using the getResultValue(int index) method, where the index can range between 0 and getResultCount() - 1. For value options that also accept details, these can be similarly accessed using the getResultDetail(int index) method.

The OptionSet class

The OptionSet class is basically a container for a set of OptionData instances and also the data arguments found on the command line.

The constructor has the form:

OptionSet(Options.Prefix prefix, Options.Multiplicity defaultMultiplicity, String setName, int minData, int maxData) 

Again, this constructor has package access. Option sets can only be created through the Options class's different addSet() methods. The default multiplicity for the options specified here can be overridden when adding an option to the set. The set name specified here is a unique identifier used to refer to the set. minData and maxData are the minimum and maximum number of acceptable data arguments for this set.

The public API for OptionSet contains the following methods:

General access methods:

String getSetName() int getMinData() int getMaxData() 

Methods to add options:

OptionSet addOption(String key) OptionSet addOption(String key, Multiplicity multiplicity) OptionSet addOption(String key, Separator separator) OptionSet addOption(String key, Separator separator, Multiplicity multiplicity) OptionSet addOption(String key, boolean details, Separator separator) OptionSet addOption(String key, boolean details, Separator separator, Multiplicity multiplicity) 

Methods to access check result data:

java.util.ArrayList getOptionData() OptionData getOption(String key) boolean isSet(String key) java.util.ArrayList getData() java.util.ArrayList getUnmatched() 

Note that the methods for adding options that take a Separator argument create an OptionData instance accepting a value. The addOption() methods return the set instance itself, which allows invocation chaining:

Options options = new Options(args); options.addSet("MySet").addOption("a").addOption("b"); 

After the checks have been performed, their results are available through the remaining methods. getOptionData() returns a list of all OptionData instances, while getOption() allows direct access to a specific option. isSet(String key) is a convenience method that checks whether an options was found at least once on the command line. getData() provides access to the data arguments found, while getUnmatched() lists all options found on the command line for which no matching OptionData instances were found.

The Options class

Options is the core class with which applications will interact. It provides several constructors, all of which take the command line argument string array that the main() method provides as the first argument:

Options(String args[]) Options(String args[], int data) Options(String args[], int defMinData, int defMaxData) Options(String args[], Multiplicity defaultMultiplicity) Options(String args[], Multiplicity defaultMultiplicity, int data) Options(String args[], Multiplicity defaultMultiplicity, int defMinData, int defMaxData) Options(String args[], Prefix prefix) Options(String args[], Prefix prefix, int data) Options(String args[], Prefix prefix, int defMinData, int defMaxData) Options(String args[], Prefix prefix, Multiplicity defaultMultiplicity) Options(String args[], Prefix prefix, Multiplicity defaultMultiplicity, int data) Options(String args[], Prefix prefix, Multiplicity defaultMultiplicity, int defMinData, int defMaxData) 

The first constructor in this list is the simplest one using all the default values, while the last one is the most generic.

Table 1: Arguments for the Options() constructors and their meaning

Value Description Default
prefix This constructor argument is the only place where a prefix can be specified. This value is passed on to any option set and any option created subsequently. The idea behind this approach is that within a given application, it proves unlikely that different prefixes will need to be used. Prefix.DASH
defaultMultiplicity This default multiplicity is passed to each option set and used as the default for options added to a set without specifying a multiplicity. Of course, this multiplicity can be overridden for each option added. Multiplicity.ONCE
defMinData defMinData is the default minimum number of supported data arguments passed to each option set, but it can of course be overridden when adding a set. 0
defMaxData defMaxData 각 옵션 집합에 전달되는 지원되는 데이터 인수의 기본 최대 수이지만 집합을 추가 할 때 물론 재정의 할 수 있습니다. 0