문제

Java에서 문자열을 1024바이트 단위로 분할하는 효율적인 방법은 무엇입니까?두 개 이상의 청크가 있는 경우 헤더(고정 크기 문자열)는 모든 후속 청크에서 반복되어야 합니다.

도움이 되었습니까?

해결책

줄과 바이트는 완전히 다른 두 가지이므로 문자열을 바이트로 나누기를 원하는 것은 그림을 구절로 나누고 싶어하는 것만 큼 의미가 없습니다.

실제로하고 싶은 것은 무엇입니까?

문자열과 바이트 사이를 변환하려면 문자열의 모든 문자를 인코딩 할 수있는 인코딩을 지정해야합니다. 인코딩과 캐릭터에 따라 일부는 둘 이상의 바이트에 걸쳐있을 수 있습니다.

문자열을 1024 자 덩어리로 나누고 바이트로 인코딩 할 수 있지만 각 청크는 1024 바이트 이상일 수 있습니다.

또는 원래 문자열을 바이트로 인코딩 한 다음 1024의 덩어리로 나눌 수 있지만, 전체를 문자열로 다시 디코딩하기 전에 바이트로 추가해야합니다. 캐릭터는 1 바이트 이상에 걸쳐 있습니다.

문자열이 매우 길어질 때 메모리 사용이 걱정된다면, 데이터를 여러 번 사본으로 유지하지 않기 위해 스트림 (java.io 패키지)을 EN/디코딩 및 분할에 사용해야합니다. 이상적으로는 원래 문자열을 한 조각으로 전혀 피하고 대신 스트림을 사용하여 어디에서나 작은 덩어리로 읽습니다.

다른 팁

빠른 방법과 메모리를 보존하는 방법의 두 가지 방법이 있습니다.하지만 먼저 문자열에 어떤 문자가 있는지 알아야 합니다.아스키?움라우트(128에서 255 사이의 문자) 또는 유니코드(s.getChar()가 256보다 큰 것을 반환함)가 있습니까?이에 따라 다른 인코딩을 사용해야 합니다.바이너리 데이터가 있는 경우 "iso-8859-1"을 사용해 보십시오. 그러면 문자열의 데이터가 보존됩니다.유니코드를 사용하는 경우 "utf-8"을 사용해 보세요.이진 데이터를 가정하겠습니다.

String encoding = "iso-8859-1";

가장 빠른 방법:

ByteArrayInputStream in = new ByteArrayInputStream (string.getBytes(encoding));

문자열은 유니코드이므로 모든 문자에는 바이트.인코딩을 지정해야 합니다("플랫폼 기본값"에 의존하지 마세요).나중에 통증만 유발합니다.)

이제 다음을 사용하여 1024개 청크로 읽을 수 있습니다.

byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) > 0) { ... }

여기에는 원래 문자열보다 약 3배 많은 RAM이 필요합니다.

보다 메모리를 절약하는 방법은 StringReader와 OutputStreamWriter(ByteArrayOutputStream을 래핑하는)를 사용하는 변환기를 작성하는 것입니다.기본 버퍼에 하나의 데이터 청크가 포함될 때까지 판독기에서 기록기로 바이트를 복사합니다.

그렇다면 데이터를 실제 출력(헤더 앞에 추가)에 복사하고, 추가 바이트(유니코드->바이트 변환으로 생성되었을 수 있음)를 임시 버퍼에 복사하고, buffer.reset()을 호출하고 임시 버퍼를 완충기.

코드는 다음과 같습니다(테스트되지 않음).

StringReader r = new StringReader (string);
ByteArrayOutputStream buffer = new ByteArrayOutputStream (1024*2); // Twice as large as necessary
OutputStreamWriter w = new OutputStreamWriter  (buffer, encoding);

char[] cbuf = new char[100];
byte[] tempBuf;
int len;
while ((len = r.read(cbuf, 0, cbuf.length)) > 0) {
    w.write(cbuf, 0, len);
    w.flush();
    if (buffer.size()) >= 1024) {
        tempBuf = buffer.toByteArray();
        ... ready to process one chunk ...
        buffer.reset();
        if (tempBuf.length > 1024) {
            buffer.write(tempBuf, 1024, tempBuf.length - 1024);
        }
    }
}
... check if some data is left in buffer and process that, too ...

여기에는 몇 킬로바이트의 RAM만 필요합니다.

[편집] 댓글에서 문자열의 이진 데이터에 대한 긴 논의가 있었습니다.우선, 바이너리 데이터를 생성하고 어딘가에 저장할 때 주의를 기울이는 한 바이너리 데이터를 String에 넣는 것은 완벽하게 안전합니다.이러한 문자열을 생성하려면 byte[] 배열을 사용하고 다음을 수행하십시오.

String safe = new String (array, "iso-8859-1");

Java에서 ISO-8859-1(또는 ISO-Latin1)은 1:1 매핑입니다.이는 배열의 바이트가 어떤 방식으로도 해석되지 않음을 의미합니다.이제 데이터에 대해 substring() 등을 사용하거나 인덱스로 검색하고 정규식을 실행하는 등의 작업을 수행할 수 있습니다.예를 들어, 0바이트의 위치를 ​​찾으십시오.

int pos = safe.indexOf('\u0000');

이는 데이터 인코딩을 모르고 일부 코덱이 문제를 일으키기 전에 살펴보고 싶은 경우에 특히 유용합니다.

어딘가에 데이터를 쓰려면 반대 작업은 다음과 같습니다.

byte[] data = safe.getBytes("iso-8859-1");

기본 방법을 사용하지 마십시오 new String(array) 또는 String.getBytes()! 어느 날, 귀하의 코드가 다른 플랫폼에서 실행될 예정이고 작동이 중단될 것입니다.

이제 문자열에서 255보다 큰 문자 문제가 발생합니다.이 방법을 사용하면 문자열에 그러한 문자가 전혀 포함되지 않습니다.즉, 어떤 이유로든 예외가 있는 경우 ISO-Latin1에서는 모든 유니코드 문자를 표현할 방법이 없기 때문에 getBytes()는 예외를 발생시키므로 코드가 자동으로 실패하지 않는다는 점에서 안전합니다.

어떤 사람들은 이것이 충분히 안전하지 않으며 바이트와 문자열을 혼합해서는 안 된다고 주장할 수도 있습니다.요즘 시대에는 그런 사치가 없습니다.많은 데이터에는 명시적인 인코딩 정보가 없습니다. 예를 들어 파일에는 액세스 권한이나 이름이 있는 것과 같은 방식으로 "인코딩" 속성이 없습니다.XML은 명시적인 인코딩 정보가 있는 몇 안 되는 형식 중 하나이며 주석을 사용하여 이 중요한 정보를 지정하는 Emacs 또는 jEdit와 같은 편집기가 있습니다.이는 바이트 스트림을 처리할 때 항상 해당 스트림이 어떤 인코딩인지 알아야 함을 의미합니다.현재로서는 데이터의 출처에 관계없이 항상 작동하는 코드를 작성하는 것이 불가능합니다.

XML을 사용하더라도 고기를 디코딩하기 전에 파일의 헤더를 바이트로 읽어서 인코딩을 결정해야 합니다.

중요한 점은 처리해야 하는 데이터 스트림을 생성하는 데 어떤 인코딩이 사용되었는지 앉아서 파악하는 것입니다.그렇게 하면 다행이고, 그렇지 않으면 망합니다.혼란은 대부분의 사람들이 동일한 바이트가 인코딩에 따라 다른 것을 의미할 수 있다는 사실이나 심지어 두 개 이상의 인코딩이 있다는 사실을 인식하지 못한다는 사실에서 비롯됩니다.또한 Sun이 "플랫폼 기본 인코딩"이라는 개념을 도입하지 않았다면 도움이 되었을 것입니다.

초보자를 위한 중요 사항:

  • 인코딩(문자 집합)이 두 개 이상 있습니다.
  • 영어에서 사용하는 것보다 더 많은 문자가 있습니다.심지어 여러 가지가 있습니다 숫자 세트 (ASCII, 전체 너비, 아랍어-인도어, 벵골어).
  • 처리 중인 데이터를 생성하는 데 어떤 인코딩이 사용되었는지 알아야 합니다.
  • 처리 중인 데이터를 쓰기 위해 어떤 인코딩을 사용해야 하는지 알아야 합니다.
  • 다음 프로그램이 출력(XML 헤더, HTML 메타 태그, 특수 인코딩 주석 등)을 디코딩할 수 있도록 이 인코딩 정보를 지정하는 올바른 방법을 알아야 합니다.

ASCII 시대는 끝났습니다.

나는 내가 늦었다는 것을 알고 있지만, 나는 솔루션을 직접 찾고 있었고 내 대답을 베스트 답으로 찾았습니다.

private static String chunk_split(String original, int length, String separator) throws IOException {
    ByteArrayInputStream bis = new ByteArrayInputStream(original.getBytes());
    int n = 0;
    byte[] buffer = new byte[length];
    String result = "";
    while ((n = bis.read(buffer)) > 0) {
        for (byte b : buffer) {
            result += (char) b;
        }
        Arrays.fill(buffer, (byte) 0);
        result += separator;
    }
    return result;
}

예시:

public static void main(String[] args) throws IOException{
       String original = "abcdefghijklmnopqrstuvwxyz";
       System.out.println(chunk_split(original,5,"\n"));
}

산출:

abced
fghij
klmno
pqrst
uvwxy
z

나는 이것을 스스로 시도하고 있었는데, 나는 1MB의 거대한 끈 (거의 10MB)을 청크해야한다. 이렇게하면 최소한의 시간 내에 데이터를 징수 할 수 있습니다. (1 초 미만).

private static ArrayList<String> chunkLogMessage(String logMessage) throws Exception {
    ArrayList<String> messages = new ArrayList<>();
    if(logMessage.getBytes().length > CHUNK_SIZE) {
        Log.e("chunk_started", System.currentTimeMillis()+"");
        byte[] buffer = new byte[CHUNK_SIZE];
        int start = 0, end = buffer.length;
        long remaining = logMessage.getBytes().length;
        ByteArrayInputStream inputStream = new ByteArrayInputStream(logMessage.getBytes());
        while ((inputStream.read(buffer, start, end)) != -1){
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            outputStream.write(buffer, start, end);
            messages.add(outputStream.toString("UTF-8"));
            remaining = remaining - end;
            if(remaining <= end){
                end = (int) remaining;
            }
        }
        Log.e("chunk_ended", System.currentTimeMillis()+"");
        return messages;
    }
    messages.add(logMessage);
    return messages;
}

logcat :

22:08:00.262 3382-3425/com.sample.app E/chunk_started: 1533910080261
22:08:01.228 3382-3425/com.sample.app E/chunk_ended: 1533910081228
22:08:02.468 3382-3425/com.sample.app E/chunk_started: 1533910082468
22:08:03.478 3382-3425/com.sample.app E/chunk_ended: 1533910083478
22:09:19.801 3382-3382/com.sample.app E/chunk_started: 1533910159801
22:09:20.662 3382-3382/com.sample.app E/chunk_ended: 1533910160662

그렇습니다. 위의 모든 것이 확실히 작동하는 것은 아니더라도 대부분이 효과가있을 것입니다.

또는 체크 아웃 할 수 있습니다 이것 정확히 그렇게하는 프로젝트; 오직 문자열뿐만 아니라 바이트 어레이, 입력 스트림 및 파일도 청크 할 수 있습니다.

2 개의 클래스가 있습니다. DataChunker 그리고 StringChunker


DataChunker chunker = new DataChunker(8192, blob) {
@Override 
public void chunkFound(byte[] foundChunk, int bytesProcessed) {
//process chunk here
}
@Override 
public void chunksExhausted(int bytesProcessed) { 
//called when all the blocks have been exhausted
} 
};

String blob = "Experience is wasted if history does not repeat itself...Gbemiro Jiboye";

 final StringBuilder builder = new StringBuilder();
        StringChunker chunker = new StringChunker(4, blob) {
            @Override
            public void chunkFound(String foundChunk, int bytesProcessed) {
                builder.append(foundChunk);
                System.out.println("Found: "+foundChunk+", bytesProcessed: "+bytesProcessed+" bytes");
            }

            @Override
            public void chunksExhausted(int bytesProcessed) {
                System.out.println("Processed all of: "+bytesProcessed+" bytes. Rebuilt string is: "+builder.toString());
            }
        };

그만큼 blob 생성자에서 Datachunker's 생성자는 바이트 어레이, a File 또는 InputStream

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top