Java'da iki InputStream'e sahip olmak, onları birleştirmenin bir yolu var, böylece her iki akışın da çıkışını veren bir InputStream ile bitiyorsunuz? Nasıl?Java'da iki giriş akışını nasıl birleştiriyorsunuz?
cevap
Açıklandığı gibi, birleştirme ile ne demek istediğiniz açık değildir.
"Rastgele" kullanılabilir girişinin alınması, size yararlı bir yanıt vermek ve akışların engellenmesini engellemek için InputStream.available
tarafından karmaşıktır. Akarsulardan okumak için iki konuya ihtiyaç duyarsınız ve daha sonra veriyi java.io.Piped(In|Out)putStream
(bu sınıfların sorunları olsa da) üzerinden geçirebilirsiniz. Alternatif olarak bazı akış tipleri için farklı bir arayüzün kullanılması mümkün olabilir, örneğin java.nio
bloke edici olmayan kanallar.
İlk giriş akışının tam içeriğini ve ardından ikinci olmasını istiyorsanız: new java.io.SequenceInputStream(s1, s2)
. İhtiyacınız olan şey olabilir.
Oh, çok güzel, yeni bir şey öğrendim. SequenceInputStream esas olarak, CatInputStream'imle aynıdır, ancak bir LinkedList yerine, eski Enumerations kullanın. :-) –
Cevabınızın ilk bölümüne bir saldırı olarak, genel durumda çözmek zor ama belirli durumlarda FileInputStream (ve belki de yuvaları?) Için instanceof/cast ve dışında bir kanal oluşturabilirsiniz. (Diğer akışlar, tutarlı bir arabirim oluşturmak için Channels.newChannel'i kullanabilir, ancak gerekli olmayan engelleme niteliklerine sahip olmaz.) –
Collections.enumeration sizin arkadaşınızdır. İlk bölümümün bir kısmını unuttum - düzenleyecek. –
Düşünebildiğim kadarıyla. Muhtemelen iki akışın içeriğini bir bayt [] içine okumalı ve bundan sonra bir ByteArrayInputStream oluşturmalısınız.
Kolay anlaşılması kolay, uygulanabilir bir çözüm için tekrar. Engelleme önemliyse (veya çok büyükse) gerekli davranışa sahip olamaz. –
Bunu yapan bir özel InputStream
uygulaması yazabilirsiniz. Örnek:
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Deque;
import java.util.LinkedList;
public class CatInputStream extends InputStream {
private final Deque<InputStream> streams;
public CatInputStream(InputStream... streams) {
this.streams = new LinkedList<InputStream>();
Collections.addAll(this.streams, streams);
}
private void nextStream() throws IOException {
streams.removeFirst().close();
}
@Override
public int read() throws IOException {
int result = -1;
while (!streams.isEmpty()
&& (result = streams.getFirst().read()) == -1) {
nextStream();
}
return result;
}
@Override
public int read(byte b[], int off, int len) throws IOException {
int result = -1;
while (!streams.isEmpty()
&& (result = streams.getFirst().read(b, off, len)) == -1) {
nextStream();
}
return result;
}
@Override
public long skip(long n) throws IOException {
long skipped = 0L;
while (skipped < n && !streams.isEmpty()) {
int thisSkip = streams.getFirst().skip(n - skipped);
if (thisSkip > 0)
skipped += thisSkip;
else
nextStream();
}
return skipped;
}
@Override
public int available() throws IOException {
return streams.isEmpty() ? 0 : streams.getFirst().available();
}
@Override
public void close() throws IOException {
while (!streams.isEmpty())
nextStream();
}
}
Bu kod test edilmemiştir, bu nedenle kilometre durumunuz değişebilir.
Bu, Merzbow'un önerdiği gibi, SequenceInputStream ile aynı şeyi yapmaz mı? –
Üzgünüz, ancak tackline önce SequenceInputStream'i önerdi (ve bunun için onu + 1'ledim). SO'da, en erken iyi cevap kazanır; Daha sonraki cevapların intihal olup olmadığını asla bilemezsiniz. Ayrıca, tackline'ın SequenceInputStream ve CatInputStream (Collections.enumeration'ı kullanma konusundaki kararını alacağım) arasında bir karşılaştırma için yaptığım cevabı da okuyun. –
Cevabımı reddeden kişi benim son yorumumdan dolayı yaptıysa özür dilerim; Daha iyi bir şekilde açıklamalıyım: eğer daha önce yayınlanmış bir cevabın bir kopyası (veya alt kümesi) olarak ortaya çıkan bir cevap yazıyorsam, genellikle onu siliyorum, bunun için hiçbir puan alamayacağım. SO üzerinde, gerçekten "Batı'da En Hızlı Silah" (bu soru başlığını aratın). –
java.io.SequenceInputStream
Akarsu sayımını kabul eder ve ilk akışın içeriğini, sonra ikinciyi ve tüm akarsular boş olana kadar çıkarır.
Bayt dizilerine özgü bir MVar uygulaması (kendi paket tanımınızı eklediğinizden emin olun). Buradan, birleştirilmiş akışlar üzerinde bir giriş akışı yazmak önemsizdir. İstediğim takdirde bunu da gönderebilirim.
import java.nio.ByteBuffer;
public final class MVar {
private static enum State {
EMPTY, ONE, MANY
}
private final Object lock;
private State state;
private byte b;
private ByteBuffer bytes;
private int length;
public MVar() {
lock = new Object();
state = State.EMPTY;
}
public final void put(byte b) {
synchronized (lock) {
while (state != State.EMPTY) {
try {
lock.wait();
} catch (InterruptedException e) {}
}
this.b = b;
state = State.ONE;
lock.notifyAll();
}
}
public final void put(byte[] bytes, int offset, int length) {
if (length == 0) {
return;
}
synchronized (lock) {
while (state != State.EMPTY) {
try {
lock.wait();
} catch (InterruptedException e) {}
}
this.bytes = ByteBuffer.allocateDirect(length);
this.bytes.put(bytes, offset, length);
this.bytes.position(0);
this.length = length;
state = State.MANY;
lock.notifyAll();
}
}
public final byte take() {
synchronized (lock) {
while (state == State.EMPTY) {
try {
lock.wait();
} catch (InterruptedException e) {}
}
switch (state) {
case ONE: {
state = State.EMPTY;
byte b = this.b;
lock.notifyAll();
return b;
}
case MANY: {
byte b = bytes.get();
state = --length <= 0 ? State.EMPTY : State.MANY;
lock.notifyAll();
return b;
}
default:
throw new AssertionError();
}
}
}
public final int take(byte[] bytes, int offset, int length) {
if (length == 0) {
return 0;
}
synchronized (lock) {
while (state == State.EMPTY) {
try {
lock.wait();
} catch (InterruptedException e) {}
}
switch (state) {
case ONE:
bytes[offset] = b;
state = State.EMPTY;
lock.notifyAll();
return 1;
case MANY:
if (this.length > length) {
this.bytes.get(bytes, offset, length);
this.length = this.length - length;
synchronized (lock) {
lock.notifyAll();
}
return length;
}
this.bytes.get(bytes, offset, this.length);
this.bytes = null;
state = State.EMPTY;
length = this.length;
lock.notifyAll();
return length;
default:
throw new AssertionError();
}
}
}
}
Tam olarak nasıl birleştirme? İlk okunduktan sonra kesintisiz olarak ikinci bir akıştan okumaya devam etmek ister misiniz? Java'ya çok aşina değilim, fakat C# 'da, her iki taban akışına referanslar içeren Akışı'ndan gelen ve Okuma yöntemini geçersiz kılan bir sınıfı uygulayarak bunu kolayca yapabilirdiniz. – Noldorin