Chciałbym czytać wiele obiektów JSON z pliku / strumienia w Pythonie, po jednym naraz. Niestety json.load()
tylko .read()
do końca pliku; wydaje się, że nie ma sposobu, aby użyć go do odczytania pojedynczego obiektu lub leniwego iterowania po obiektach.
Czy jest na to sposób? Korzystanie z biblioteki standardowej byłoby idealne, ale jeśli istnieje biblioteka innej firmy, użyłbym jej zamiast tego.
W tej chwili umieszczam każdy obiekt w osobnej linii i używam json.loads(f.readline())
, ale naprawdę wolałbym nie potrzebować tego robić.
Przykładowe zastosowanie
example.py
import my_json as json
import sys
for o in json.iterload(sys.stdin):
print("Working on a", type(o))
in.txt
{"foo": ["bar", "baz"]} 1 2 [] 4 5 6
przykładowa sesja
$ python3.2 example.py < in.txt
Working on a dict
Working on a int
Working on a int
Working on a list
Working on a int
Working on a int
Working on a int
python
json
serialization
Jeremy
źródło
źródło
{"foo": ["bar", "baz"]}
w moim przykładzie), powinienyield
to zrobić, a następnie przejść do następnego (1
).'\n'
(pojedynczej nowej linii, a nie dwóch znaków) w swojej reprezentacji json, ponieważ'\n'
musi być umieszczony wewnątrz łańcucha json i dlatego'\n'
może być używany tylko do formatowania np. Uważam, żejson.dumps()
nie t wprowadzaj'\n'
domyślnie. Uważaj, że znaki nowej linii Unicode, takie jak U + 0085, mogą znajdować się wewnątrz ciągów json.Odpowiedzi:
Oto dużo, dużo prostsze rozwiązanie. Sekret polega na tym, aby spróbować, nie powiodło się i użyć informacji z wyjątku do poprawnego przeanalizowania. Jedynym ograniczeniem jest to, że plik musi być możliwy do przeszukania.
Edycja: właśnie zauważyłem, że będzie to działać tylko dla Pythona> = 3.5. Wcześniej błędy zwracają błąd ValueError i trzeba wyodrębnić pozycję z ciągu, np
źródło
re
nie zadziała - ukośniki odwrotne muszą uciec. Rozważmy surowy ciągr'...'
.ujson
zamiastjson
ciebie, uzyskasz ogromne przyspieszenieJSON generalnie nie jest zbyt dobry do tego rodzaju przyrostowego użycia; nie ma standardowego sposobu serializacji wielu obiektów, aby można je było łatwo ładować pojedynczo, bez analizowania całej partii.
Rozwiązanie obiektu na linię, którego używasz, jest również widoczne w innym miejscu. Scrapy nazywa to „liniami JSON”:
Możesz to zrobić nieco bardziej w Pythonie:
Myślę, że to najlepszy sposób - nie opiera się na bibliotekach innych firm i łatwo jest zrozumieć, co się dzieje. Użyłem go również w moim własnym kodzie.
źródło
Może trochę późno, ale miałem dokładnie ten problem (cóż, mniej więcej). Moim standardowym rozwiązaniem tych problemów jest zwykle podzielenie wyrażeń regularnych na dobrze znanym obiekcie root, ale w moim przypadku było to niemożliwe. Jedynym możliwym sposobem zrobienia tego w sposób ogólny jest zaimplementowanie odpowiedniego tokenizera .
Po tym, jak nie znalazłem wystarczająco ogólnego i dość dobrego rozwiązania, zakończyłem to samodzielnie, pisząc plik
splitstream
moduł. Jest to pre-tokenizer, który rozumie JSON i XML i dzieli ciągły strumień na wiele fragmentów do analizy (pozostawia jednak faktyczną analizę Tobie). Aby uzyskać z tego jakąś wydajność, jest napisany jako moduł C.Przykład:
źródło
Jasne, że możesz to zrobić. Musisz tylko wziąć
raw_decode
bezpośrednio. Ta implementacja ładuje cały plik do pamięci i operuje na tym ciągu (podobnie jakjson.load
); jeśli masz duże pliki, możesz je zmodyfikować, aby w razie potrzeby czytać tylko z pliku, bez większych trudności.Sposób użycia: tak jak prosiłeś, jest to generator.
źródło
Jest to dość nieprzyjemny problem, ponieważ musisz przesyłać strumieniowo w wierszach, ale dopasowywanie wzorca w wielu wierszach z nawiasami klamrowymi, ale także json z dopasowaniem do wzorca. Jest to rodzaj json-preparse, po którym następuje analiza json. Json, w porównaniu do innych formatów, jest łatwy do przeanalizowania, więc nie zawsze jest konieczne sięganie po bibliotekę analizującą, niemniej jednak, jak rozwiązać te sprzeczne problemy?
Generatory na ratunek!
Piękno generatorów dla takiego problemu polega na tym, że można je układać jeden na drugim, stopniowo odejmując trudność problemu, zachowując jednocześnie lenistwo. Rozważałem również użycie mechanizmu przekazywania wartości z powrotem do generatora (send ()), ale na szczęście okazało się, że nie muszę tego używać.
Aby rozwiązać pierwszy z problemów, potrzebujesz jakiegoś narzędzia do wyszukiwania strumieniowego, jako strumieniowej wersji re.finditera. Poniższa próba polega na wciągnięciu wierszy w razie potrzeby (odkomentuj instrukcję debugowania, aby zobaczyć), jednocześnie zwracając dopasowania. Właściwie zmodyfikowałem go nieco, aby uzyskać niedopasowane wiersze, a także dopasowania (oznaczone jako 0 lub 1 w pierwszej części uzyskanej krotki).
Dzięki temu możliwe jest dopasowanie do nawiasów klamrowych, za każdym razem uwzględnienie, czy nawiasy są zrównoważone, a następnie zwrócenie odpowiednio obiektów prostych lub złożonych.
Zwraca krotki w następujący sposób:
Zasadniczo to jest paskudna część zrobiona. Teraz musimy po prostu wykonać ostatni poziom analizy, jaki uznamy za stosowny. Na przykład możemy użyć funkcji iterload Jeremy'ego Romana (dzięki!), Aby przeanalizować pojedynczą linię:
Sprawdź to:
Otrzymuję następujące wyniki (a jeśli włączysz tę linię debugowania, zobaczysz, że w razie potrzeby wciąga linie):
To nie zadziała we wszystkich sytuacjach. Ze względu na implementację
json
biblioteki niemożliwe jest całkowicie poprawne działanie bez ponownego zaimplementowania parsera.źródło
"}"
i"]"
występują w ciągach JSON? Myślę, że jest to ogólne ograniczenie analizowania za pomocą wyrażenia regularnego.Uważam, że lepszym sposobem na zrobienie tego byłoby użycie maszyny stanowej. Poniżej znajduje się przykładowy kod, który opracowałem, konwertując kod NodeJS na poniższy link do Python
3 (użyte słowo kluczowe nonlocal dostępne tylko w Pythonie 3, kod nie będzie działał w Pythonie 2)Edit-1: Zaktualizowany i zgodny kod z Pythonem 2
Edit-2: Zaktualizowano i dodano również wersję tylko dla Pythona3
https://gist.github.com/creationix/5992451
Tylko wersja Python 3
Wersja zgodna z Python 2
Testuję to
Wynik tego samego jest
źródło
Chciałbym przedstawić rozwiązanie. Kluczową myślą jest „próba” dekodowania: jeśli się nie powiedzie, podaj więcej sygnału, w przeciwnym razie użyj informacji o przesunięciu do przygotowania następnego dekodowania.
Jednak obecny moduł json nie może tolerować SPACJI w nagłówku ciągu do zdekodowania, więc muszę je usunąć.
========================= Przetestowałem kilka plików txt i działa dobrze. (in1.txt)
(in2.txt)
(in.txt, twoja inicjał)
(wyjście do przypadku testowego Benedicta)
źródło
To moje:
źródło
Użyłem eleganckiego rozwiązania @ wuilang. Proste podejście - przeczytaj bajt, spróbuj zdekodować, przeczytaj bajt, spróbuj dekodować, ... - działało, ale niestety było bardzo wolne.
W moim przypadku próbowałem odczytać „ładnie wydrukowane” obiekty JSON tego samego typu z pliku. Pozwoliło mi to zoptymalizować podejście; Mogłem odczytać plik wiersz po wierszu, dekodując tylko wtedy, gdy znalazłem wiersz zawierający dokładnie „}”:
Jeśli zdarza się, że pracujesz z kompaktowym kodem JSON (jeden w każdym wierszu), który wymyka znaki nowej linii w literałach ciągów, możesz bezpiecznie uprościć to podejście jeszcze bardziej:
Oczywiście te proste metody działają tylko w przypadku bardzo konkretnych rodzajów JSON. Jeśli jednak te założenia się utrzymają, rozwiązania te działają poprawnie i szybko.
źródło
Jeśli używasz instancji json.JSONDecoder, możesz użyć
raw_decode
funkcji składowej . Zwraca krotkę reprezentacji w języku Python wartości JSON i indeksu, do którego zatrzymano analizowanie. Ułatwia to wycinanie (lub wyszukiwanie w obiekcie strumienia) pozostałych wartości JSON. Nie jestem tak zadowolony z dodatkowej pętli while, aby pomijać białe znaki między różnymi wartościami JSON w danych wejściowych, ale moim zdaniem wykonuje to zadanie.Następna wersja jest znacznie krótsza i zjada część ciągu, która jest już przeanalizowana. Wygląda na to, że z jakiegoś powodu drugie wywołanie json.JSONDecoder.raw_decode () wydaje się nie powieść, gdy pierwszy znak w ciągu jest białą spacją, jest to również powód, dla którego pomijam białe znaki w whileloop powyżej ...
W dokumentacji dotyczącej klasy json.JSONDecoder metoda raw_decode https://docs.python.org/3/library/json.html#encoders-and-decoders zawiera:
Te zbędne dane mogą z łatwością być kolejną wartością JSON. Innymi słowy, metoda może być napisana w tym celu.
Z input.txt używając górnej funkcji otrzymuję przykładowe dane wyjściowe przedstawione w pierwotnym pytaniu.
źródło
Możesz użyć https://pypi.org/project/json-stream-parser/ dokładnie w tym celu.
wynik
źródło