행이 많은 데이터 파일이 있는데 필드 목록이 다를 수 있습니다. 다음은 샘플 라인 형식입니다. 각 필드는 다음으로 구분됩니다 @@@
.
runAs="X094174"@@@format="excel2007"@@@path="/Path1"@@@name="X143122"@@@name="X182881"@@@name="X094174"@@@address="[email protected]"@@@address="[email protected]"@@@AgentLoc="/loc1"
데이터베이스 테이블(열/행 형식)과 같은 형식으로 데이터를 가져오고 싶습니다.
runAs format path AgentLoc name address
X094174 excel2007 /Path1 /loc1 X143122 [email protected]
X094174 excel2007 /Path1 /loc1 X182881 [email protected]
X094174 excel2007 /Path1 /loc1 X094174
파일 읽기 루프와 awk
.
아래와 같은 형식으로 데이터를 생성하는 것이 용이하다면,
runAs format path AgentLoc name address
X094174 excel2007 /Path1 /loc1 X143122
X094174 excel2007 /Path1 /loc1 X182881
X094174 excel2007 /Path1 /loc1 X094174
X094174 excel2007 /Path1 /loc1 [email protected]
X094174 excel2007 /Path1 /loc1 [email protected]
답변1
이것앗필요한 양식을 생성합니다.
$ cat dat
runAs="X094174"@@@format="excel2007"@@@path="/Path1"@@@name="X143122"@@@name="X182881"@@@name="X094174"@@@address="[email protected]"@@@address="[email protected]"@@@AgentLoc="/loc1"
먼저 데이터를 행당 하나의 항목 형식으로 배치합니다.
$ awk -F '@@@' '{ for(i=1;i<=NF;i++){ print $i } }' dat > tmp.dat
그런 다음 테이블을 작성하고 행 끝을 정리합니다.
$ awk -F '=' '{
head[$1]++;
dat[$1,head[$1]]=$2
} END{
max=0;
for(i in head){
printf i"\t"
}
print "";
for(i in dat){
split(i, arr_i, SUBSEP);
if(arr_i[2]>max){
max=arr_i[2]
}
}
for(j=1;j<=max;j++){
for(i in head){
if(head[i]==1){
printf dat[i,1]"\t"
}else{
printf dat[i,j]"\t"
}
}
print ""
}
}' tmp.dat | awk -F '\t' '{ for(i=1;i<NF;i++){ printf $i"\t" } print $NF }' > dat.xls
$ cat dat.xls
runAs format address AgentLoc name path
"X094174" "excel2007" "[email protected]" "/loc1" "X143122" "/Path1"
"X094174" "excel2007" "[email protected]" "/loc1" "X182881" "/Path1"
"X094174" "excel2007" "/loc1" "X094174" "/Path1"
예를 들어 Excel로 가져온 후 TAB 중지 구분 기호를 선택합니다.
값이 나타나는 순서에 따라 테이블의 행이 관련되는 방식이 결정됩니다.
tmp.dat
파이프를 사용하면 위의 작업을 한 단계로 수행할 수 있으므로 임시 파일을 피할 수 있습니다.
답변2
Java 11부터 다음이 있습니다.텍스트 파일에서 직접 Java 코드를 실행하기 위한 깔끔한 기능, 그렇다면 이 문제를 해결하기 위해 Java를 사용하는 것은 어떨까요?
Java 11이 설치되어 있다고 가정하고 아래 코드를 저장 ConvertToTable.java
하고 다음과 같이 터미널 세션에서 실행합니다.
java ConvertToTable.java < /path/to/input.txt | tee /path/to/output.txt
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* <p>
* Reads STDIN, converts it to the desired table representation, and writes the result to STDOUT.
* </p>
* <p>
* Assumes that the incoming data uses the system character set and the system line separator.
* </p>
* <p>
* Requires Java 11. Example invocation in Bash:
* </p>
*
* <pre>
* java ConvertToTable.java < /path/to/input.txt | tee /path/to/output.txt
* </pre>
*
* @author eomanis
*/
public class ConvertToTable {
private static final Pattern FIELD_SEPARATOR_INPUT = Pattern.compile( "@@@", Pattern.LITERAL );
private static final Pattern KEY_VALUE_SEPARATOR = Pattern.compile( "=", Pattern.LITERAL );
private static final Pattern VALUE_IN_DOUBLE_QUOTES = Pattern.compile( "^\"(.*)\"$" ); // Captures the value in capturing group 1
private static final String FIELD_SEPARATOR_OUTPUT = "\t";
public static void main( String[] args ) {
Matcher matcherValueInDoubleQuotes = VALUE_IN_DOUBLE_QUOTES.matcher( "" );
Map<String, List<String>> keysAndValues = new LinkedHashMap<>(); // A map that maps a key to a list of values
boolean firstLine = true;
String line;
String[] keyAndValue;
String key;
String value;
int outputLinesCount;
try (BufferedReader reader = new BufferedReader( new InputStreamReader( System.in ) )) {
// Read and convert the incoming data, one line at a time
while ((line = reader.readLine()) != null) { // For each line...
// Discard the previous line's data
keysAndValues.values().stream().forEach( List::clear );
// Collect the line's keys and values into the map
for (String field : FIELD_SEPARATOR_INPUT.split( line )) { // For each key=value in the text line...
keyAndValue = KEY_VALUE_SEPARATOR.split( field, 2 ); // Split key=value into key and value
key = keyAndValue[0];
value = keyAndValue[1];
// Strip the double quotes from the value
if (matcherValueInDoubleQuotes.reset( value ).matches()) {
value = matcherValueInDoubleQuotes.group( 1 );
}
// Add the value to the key's list of values
if (!keysAndValues.containsKey( key )) { // If required, create a new empty list in the map for the key
keysAndValues.put( key, new ArrayList<>() );
}
keysAndValues.get( key ).add( value );
}
// First line: Generate and write the column headers (assume that the first line contains all possible keys)
if (firstLine) {
firstLine = false;
String columnHeaders = keysAndValues.keySet().stream().collect( Collectors.joining( FIELD_SEPARATOR_OUTPUT ) );
System.out.println( columnHeaders );
}
// Figure out how many output lines we will be writing for the single input line
outputLinesCount = keysAndValues.values().stream().mapToInt( List::size ).max().getAsInt();
// Write the output line(s)
for (int index = 0; index < outputLinesCount; index++) {
int indexFinal = index;
String outputLine = keysAndValues.values().stream() //
.map( list -> getValue( indexFinal, list ) ) //
.collect( Collectors.joining( FIELD_SEPARATOR_OUTPUT ) );
System.out.println( outputLine );
}
}
} catch (IOException e) {
throw new RuntimeException( e );
}
}
/**
* @return The value for the given index, with certain workarounds
*/
private static String getValue( int index, List<String> values ) {
if (values.isEmpty()) {
// The text line did not contain the key at all
return "";
} else if (values.size() == 1) {
// Value of a key that occurred exactly once in the text line: These are repeated on all output rows
return values.get( 0 );
} else {
// Value of a key that occurred multiple times in the text line: Only print them for their respective output row
return (index < values.size()) ? values.get( index ) : "";
}
}
}