알고리즘은 아니고 난해한 문제인데 이런문제를 코딩도장 회원님들은 어떻게 해결하시는지 한수 배우고 싶어 올려봅니다.
서버에서 JSON 타입의 데이터가 담긴 Array 형태의 문자열을 클라이언트 도구로 전달해 줍니다. 이때 서버에서 설정한 버퍼( ex)32 byte )의 양만큼 JSON 문자열이 Stream 형태로 전달되는데 다음과 같이 블록단위로 끊어서 전달되는것이 아닌 이전에 보낸데이터와 추가로 보내는 데이터가 규칙없이 버퍼길이만큼 중첩되어 매번 보내집니다.
1st : '[ { "seq" : 0, "content" : "abcd"'
2nd : '[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "d"'
3rd : '[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content"'
.
.
.
last : '[ { "seq" : 0, "content" : "abcd" }, ......................{ "seq" : n, "content" : "rtyy" } ]'
위 예처럼 들어온 문자열을 다음과 같이 JSON 객체로 인스턴스화 될 수 있도록(언어별 제공되는 JSON Parser 로 처리할 수 있도록) 문자열을 최소 블록별로 자르고 중복전송되는 데이터는 버리고 Array 형태로 만들어 줘야 합니다.
1st : X
2nd : [ { "seq" : 0, "content" : "abcd" } ]
3rd : [ { "seq" : 1, "content" : "dsfswde" } ]
.
.
.
last : [ { "seq" : n, "content" : "rtyy" } ]
감사합니다.
21개의 풀이가 있습니다.
VB.NET
Sub Main()
Dim arr As New List(Of String)
Dim json As String = My.Computer.FileSystem.ReadAllText("C:\Users\진용\Desktop\example jons.txt")
Dim arD() As String = Split(json, "[")
For i As Integer = 1 To arD.Length - 1
Dim bArr As New List(Of String)
Dim d() As String = Split(arD(i), "{")
For j As Integer = 0 To d.Length - 1
If d(j).Contains("}") Then
Dim itm As String = String.Format("{{ {0} }}", Trim(Split(d(j), "}")(0)))
If Not arr.Contains(itm) Then
arr.Add(itm)
bArr.Add(itm)
End If
End If
Next
Console.WriteLine(String.Format("[ {0} ]", String.Join(", ", bArr)))
Next
Console.ReadLine()
End Sub
위 데이터를 기준으로 파싱해보았습니다.
$parser = new Parse();
$parser->parse_data('[ { "seq" : 0, "content" : "abcd"');
$parser->parse_data('[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "d"');
$parser->parse_data('[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content"');
$parser->parse_data('[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content" : "rtyy" }');
$parser->printJson();
class Parse {
private $pre_data = "";
private $json = array();
function parse_data($data) {
$data = substr($data, 0, strrpos($data, "}") + 1);
if (!$data) return false;
$del_str = $this->pre_data;
$this->pre_data = $data;
$data = str_replace($del_str, "", $data);
$data = preg_replace("/\\[|\s/", "", $data);
$data = explode("},{", $data);
foreach ($data as $value) {
$value = preg_replace("/\\{|\\}|\\,/", "", $value);
if ($value) $this->json[] = "[{".$value."}]";
}
}
function printJson() {
echo "<xmp>"; var_dump($this->json); echo "</xmp>";
}
}
s477.txt
[ { "seq" : 0, "content" : "abcd"
[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "d"
[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content"
[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content" : "rtyy" } ]
s477.py
prev_complete='[ '
for line in file('s477.txt'):
p=prev_complete
while p and p[0]==line[0]:p=p[1:];line=line[1:]
buff=''
for c in line:
if c=="{" : inner = True
elif c=="}" : inner = False ;break
elif inner : buff += c
if not inner :
print '[ {'+buff+'} ]'
prev_complete += '{'+buff+'}, '
일단 들어오는 JSON 포맷이 예시와 같다고만 가정하면 들어오는데로 스캔 위치를 계속 밀어나가면서 중괄호 사이를 추출합니다. (중괄호는 중첩될 수 있다고 가정하되, 올바른 포맷으로 들어온다고 가정합니다.)
def do():
i = 0
last_i = 0
st = []
result = []
while True:
s = input()
if not s:
break
while i < len(s):
if s[i] == '{':
last_i = i
st.append('{')
elif s[i] == '}':
if st[-1] == '{':
st.pop()
if len(st) == 0:
result.append(s[last_i:i+1])
i += 1
return result
do()
그런데, 데이터가 계속 중첩되어 들어온다면 최종 데이터만 받아서 처리하면 되지 않을까도 싶네요.
Ruby
정규식으로 json 배열 획득 > set에 입력 > 버퍼별로 set에 새로 추가된 요소를 map.
require 'set'
rcved = ->buffers,sets=Set.new {
buffers.map {|s| s.gsub(/{(.|^})+?}/).to_a.delete_if {|e| !sets.add?(e) } } }
Test
buffers = [ '[ { "seq" : 0, "content" : "abcd"',
'[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "d"',
'[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content"' ]
expected = [ [],
['{ "seq" : 0, "content" : "abcd" }'],
['{ "seq" : 1, "content" : "dsfswde" }'] ]
expect( rcved[buffers] ).to eq expected
Output
#=> puts rcved[buffers] 빈 배열은 stdout에 출력되지 않음.
{ "seq" : 0, "content" : "abcd" }
{ "seq" : 1, "content" : "dsfswde" }
미완성 입력을 처리할 수 있도록 파서를 만드는 것도 좋을 것 같습니다.
약식으로 객체, 배열, 정수, 문자열 만으로 구성된 JSON을 파싱한다면..
결과를 저장할 때 complete 필드에 1(완성), 0(미완성)으로 표시하는 식이죠.
typedef struct arr_t arr_t;
typedef struct obj_t obj_t;
typedef struct json_t json_t;
struct json_t {
int type;
union {
char* s;
int n;
arr_t* a;
obj_t* o;
} value;
int complete;
};
최상위 파서 json은 첫 글자를 기준으로 arr, obj, num, str으로 분기합니다.
json_t* json(context_t *c) {
spaces(c);
if (*c->s == '[') {
c->s++;
return arr(c);
}
if (*c->s == '{') {
c->s++;
return obj(c);
}
if (*c->s == '"') {
c->s++;
return str(c);
}
if (*c->s == '-' || isdigit(*c->s)) {
return num(c);
}
assert(!"[, {, \", num required");
}
typedef struct context_t context_t;
struct context_t {
const char* s;
};
void spaces(context_t* c) {
while (*c->s == ' ')
c->s++;
}
가장 쉬운 num의 경우는 일단 시작하면 미완성을 판단하기 어려워요. -만 나오고 마는 경우 정도?
json_t* num(context_t* c) {
int sign = 1;
if (*c->s == '-') {
sign = -1;
c->s++;
}
if (*c->s == 0) {
return new_json_num(0, 0);
}
int value = 0;
while (isdigit(*c->s)) {
value *= 10;
value += *c->s++ - '0';
}
return new_json_num(value, 1);
}
str은 다음 따옴표가 나와야지 완성됩니다. (약식이라 quoted string을 제대로 지원하지는 않아요)
json_t* str(context_t* c) {
const char* begin = c->s;
while (*c->s && *c->s != '"') {
c->s++;
}
if (*c->s == 0) {
return new_json_str(begin, c->s, 0);
}
return new_json_str(begin, c->s++, 1); // skip "
}
arr은 ]가 나올때까지 ,로 구분된 json의 목록입니다.
json_t* arr(context_t* c) {
spaces(c);
arr_t *a = new_arr();
while (*c->s && *c->s != ']') {
json_t* value = json(c);
arr_add(a, value);
spaces(c);
if (*c->s == 0) {
break;
}
if (*c->s == ',') {
c->s++;
} else if (*c->s != ']'){
assert(!", or ] required");
}
}
if (*c->s == 0) // incomplete
return new_json_arr(a, 0);
c->s++; // skip ]
return new_json_arr(a, 1);
}
가장 복잡한 obj는 ':'로 구분된 key-value를 저장해야죠.
json_t* obj(context_t* c) {
spaces(c);
obj_t *o = new_obj();
while (*c->s && *c->s != '}') {
json_t* key = json(c);
assert(key->type == STR);
arr_add(o->keys, key);
spaces(c);
if (*c->s == 0) {
break;
}
if (*c->s == ':') {
c->s++;
} else {
assert(!": required");
}
spaces(c);
if (*c->s == 0) {
break;
}
json_t* value = json(c);
arr_add(o->values, value);
spaces(c);
if (*c->s == 0) {
break;
}
if (*c->s == ',') {
c->s++;
spaces(c);
} else if (*c->s != '}') {
assert(!", or } required");
}
}
if (*c->s == 0) // incomplete
return new_json_obj(o, 0);
c->s++; // skip }
return new_json_obj(o, 1);
}
이제 complete 필드를 이용하여 완성된 값인지 아닌지 판별할 수 있습니다.
문제 상황에서는 배열의 요소들이 object인데 seq 필드가 유일하게 구분가능한 값을 가진다면
쉽게 이미 처리한 것과 아닌것을 구분할 수 있을 것 같습니다.
class JsonStr():
def __init__(self):
self.pos = 0
self.str = ''
def get(self, str):
rarr = []
r = ''
pos = start = self.pos;
is_str = False
is_bs = False
for x in str[start:]:
pos+=1
if not is_str and x == '{':
r += x
elif r == '':
continue
elif not is_str and x == '}':
r += x
rarr += [r]
r = ''
self.pos = pos
continue
else:
r += x
if is_str and x == '\\':
is_bs = not is_bs
elif not is_bs and (x == "'" or x == '"'):
isstr = not is_str
print('[' + ','.join(rarr) + ']' if rarr else '')
json = JsonStr()
json.get('[ { "seq" : 0, "content" : "abcd"')
json.get('[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "d"')
json.get('[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content"}')
json.get('[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content"}, \
{ "seq" : 3, "content"}, { "seq" : 4, "content4" ')
Python 3.5.2에서 작성하였습니다.
import json
seq_arr = []
def getJsonArray(line_str):
tot_arr = []
for idx1, char1 in enumerate(line_str):
if char1 == '{' and line_str.find('}', idx1+1) >=0 :
json_string = line_str[idx1:line_str.find('}', idx1+1)+1]
obj = json.loads(json_string)
if seq_arr.count(obj['seq']) <=0:
tot_arr.append(obj)
seq_arr.append(obj['seq'])
print((lambda x:x if len(x)>0 else 'None')(tot_arr))
getJsonArray('[ { "seq" : 0, "content" : "abcd"')
getJsonArray('[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "d"')
getJsonArray('[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content"')
getJsonArray('[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content" : "dfdfdfd" }')
buf = ['[ { "seq" : 0, "content" : "abcd"',
'[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "d"',
'[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content"',
'[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content" : "dfdfdfd" }']
start = 0
bufsize = len(buf[0])
p = 0
for data in buf:
for i in range(p, len(data)):
if data[i] == '{':
start = i
elif data[i] == '}':
print('[' + data[start:i+1] + ']')
start = i + 1
p += bufsize
public class Example84 {
public static void main(String[] args) {
Example84 ex = new Example84();
String[] sArray = { "'[ { \"seq\" : 0, \"content\" : \"abcd\"'",
"'[ { \"seq\" : 0, \"content\" : \"abcd\" }, { \"seq\" : 1, \"content\" : \"d\"'",
"'[ { \"seq\" : 0, \"content\" : \"abcd\" }, { \"seq\" : 1, \"content\" : \"dsfswde\" }, { \"seq\" : 2, \"content\"'",
"'[ { \"seq\" : 0, \"content\" : \"abcd\" }, { \"seq\" : 1, \"content\" : \"dsfswde\" }, { \"seq\" : 2, \"content\" : \"sssss\" }, { \"seq\" : 3, \"content\"'" };
for (String s : sArray) {
System.out.println(ex.jsonParsing(s));
}
}
private String jsonParsing(String s) {
Pattern pattern = Pattern.compile("\\{.*?\\}");
Matcher match = pattern.matcher(s);
List<String> list = new ArrayList<>();
while (match.find()) {
list.add(match.group());
}
Collections.reverse(list);
if (list.isEmpty())
return "";
else
return String.format("[%s]", list.get(0));
}
}
정규식으로 {} 패턴을 찾아 마지막 요소만 반영 하도록 해봤습니다.
from ast import literal_eval as lit
def trans(son):
global count
global many
many+=1
tmp=son.split('}')
result='X'
if len(tmp)>count+1:
result=[lit(tmp[count][2:]+' }')]
count+=1
return('{} : {}'.format(many, result))
count=many=0
while True:
json=input('quit(q): ')
if json=='q': break
print(trans(json))
파이썬 3.6
def parser(data):
instance = []
data = list(data)
del data[0:2]
for i in data:
if i == '{':
instance.append(i)
else:
if i != ',':
instance.append(i)
if instance[len(instance)-2] == '}':
if not ''.join(instance) in data_instance:
data_instance.append(''.join(instance))
instance = []
if not data_instance:
return 'X'
else:
return [data_instance[-1]]
if __name__ == "__main__":
data_list = ['[ { "seq" : 0, "content" : "abcd"','[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "d"','[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content"']
data_instance = []
data_dic = dict()
m = 1
for i,value in enumerate(data_list):
if i == 0 :
data_dic[str(i+1)+'st'] = parser(value)
elif i == 1:
data_dic[str(i+1)+'nd'] = parser(value)
elif i == 2 :
data_dic[str(i+1)+'rd'] = parser(value)
else:
data_dic[str(i+1)+'th'] = parser(value)
print(data_dic)
{'1st': 'X', '2nd': ['{ "seq" : 0 "content" : "abcd" } '], '3rd': ['{ "seq" : 1 "content" : "dsfswde" } ']}
# 파이썬
input_sample = [
"""1st : '[ { "seq" : 0, "content" : "abcd"'""",
"""2nd : '[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "d"'""",
"""3rd : '[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content"'""",
"""last :"""
]
def for_json_parse(list1, list2, i=0, j=0):
left_end = list1[i].find(":")
left = list1[i][:left_end]
right_start = list1[i].find(": " + str(j))
right = list1[i][right_start:]
if "}" in right:
right_end = right.find("}")
right = right[:right_end]
list2.append(left + """: [ { "seq" : """ + str(j) + """, "content" """ + right + " } ] ")
i += 1
j += 1
else:
list2.append(left + ": X")
i += 1
if left != "last ":
return for_json_parse(list1, list2, i, j)
result = []
for_json_parse(input_sample, result)
for m in result: print(m)
import re
def parse(a):
l = re.findall('{.*?}', a)
b = [dict() for i in range(len(l))]
for i in range(len(l)):
l[i] = l[i].split(',')
for j in range(len(l[i])):
(key, value) = re.findall('\w+', l[i][j])
if re.match('\d', value):
value = int(value)
b[i][key] = value
if b == []:
return None
else:
return b[-1]
while 1:
print(parse(input()))
'[ { "seq" : 0, "content" : "abcd"'
'[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "d"'
'[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content"'
'[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content": "rzqw" }, { "seq" : 2,"content" : "ksav" }]'
파일에 있는거 ↑
location=input("파일위치를 입력하세요:")
f=open(location,'r')
lines=f.readlines()
lines=lines[1:]
temp_len=0
print("X")
for line in lines:
for k in range(temp_len,len(line)):
if line[k]=='{':
index1=k
if line[k]=='}':
index2=k
break
print('[ %s ]'%line[index1:index2+1])
temp_len=index2+2
Python
buf = ['[ { "seq" : 0, "content" : "abcd"',
'[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "d"',
'[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content"',
'[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content" : "dfdfdfd" }',
'[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content" : "dfdfdfd" }, { "seq" : 3, ' ]
tmp_repo = list()
for in_i, instream in enumerate(buf):
tmp_str = ""
chk = False
new = False
for c in instream:
if c == "{" or chk == True:
tmp_str += c
chk = True
if c == "}":
chk = False
if tmp_str not in tmp_repo:
tmp_repo.append(tmp_str)
new = True
tmp_str = ""
if new is False:
print("{}: X".format(in_i))
else:
print("{}: [{}]".format(in_i, tmp_repo[-1]))
정규식으로 찾았습니다. 파이썬에서는 정규식 결과가 좀 다르게 전달되네요 ...
import re
import json
data = """\
1st : '[ { "seq" : 0, "content" : "abcd"'
2nd : '[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "d"'
3rd : '[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content"'
last : '[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content":"abcd"},{ "seq" : 3, "content" : "rtyy" } ]'\
"""
history = set()
for d in data.split('\n'):
index1 = d.split(':')[0]
a = re.findall(r'\{[.]|[^{]*\}',d)
if a:
a = ['{'+i for i in a]
a = list(set(a) - history)
history = set(a) | history
a_json = json.dumps(str(a)) # json 변환 가능 테스트
print("{} : {}".format(index1, a))
else :
print("{} : X".format(index1))
sample = [
'[ { "seq" : 0, "content" : "abcd"',
'[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "d"',
'[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content"',
'[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : n, "content" : "rtyy" } ]']
result,id = [],0
for i in sample:
old = id+2
id = i.rfind('}, {') + 1
if id > 0:
if old == id+2:
if i.rfind('} ]') >= 0: result.append(''.join(['[ ',i[old:]]))
else: result.append(''.join(['[ ',i[old:id],' ]']))
else: result.append('X')
for i in result: print(i)
def passingJSON():
_indata = open('jsondata.txt','r')
_result=[]
endIndex_=-1
_stratIndex=0
while True:
line = _indata.readline()
if not line: break
aa =1
while aa > 0:
temp = line[_stratIndex:]
endIndex_ = temp.find('}')
if not endIndex_ ==-1:
_result.append(temp[temp.find('{'):endIndex_+1])
_stratIndex = endIndex_+1 +_stratIndex
aa = line[_stratIndex:].find('}')
for ii in _result:
print(ii)
passingJSON()
import re
p1 = re.compile('(?<=})[^}]+$')
p2 = re.compile('.+(?={)')
def parser(s):
s = p1.sub('', s)
s = p2.sub('', s)
s = '[ ' + s + ' ]'
return s
first = '[ { "seq" : 0, "content" : "abcd"'
second = '[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "d"'
third = '[ { "seq" : 0, "content" : "abcd" }, { "seq" : 1, "content" : "dsfswde" }, { "seq" : 2, "content"'
last = '[ { "seq" : 0, "content" : "abcd" }, ......................{ "seq" : n, "content" : "rtyy" } ]'
order =[first,second,third,last]
arr=[]
def Json (a):
s=a
while '}' in s:
st = s.index('{')
end = s.index('}')
k =s[st:end+1]
try :
s = ('').join(s.split(s[st:end+2]))
except ValueError:
pass
if k not in arr:
arr.append([k])
print([k])
for k in order:
Json(k)
결과물과 똑같이출력하는 함수는 따로 구현하진 않았구요 위에 data stream에서 식별자를 중괄호 두개로 보고, 각 data 스트림마다 여닫는 중괄호의 index를 찾아서 그 인덱스를 기준으로 슬라이싱 해서 배열에 추가해봤습니다. 질문 주시면 답변드릴게요~