Вопрос

Каков эффективный способ разбиения строки на фрагменты по 1024 байта в Java?Если имеется более одного фрагмента, то заголовок (строка фиксированного размера) необходимо повторять во всех последующих фрагментах.

Это было полезно?

Решение

Строки и байты - это две совершенно разные вещи, поэтому желание разбить строку на байты так же бессмысленно, как желание разбить картину на стихи.

Что вы на самом деле хотите сделать?

Для преобразования между строками и байтами вам необходимо указать кодировку, которая может кодировать все символы в строке.В зависимости от кодировки и символов, некоторые из них могут занимать более одного байта.

Вы можете либо разбить Строку на фрагменты по 1024 символа и закодировать их как байты, но тогда каждый фрагмент может быть больше 1024 байт.

Или вы можете закодировать исходную строку в байты, а затем разбить их на фрагменты по 1024, но тогда вам нужно обязательно добавить их в виде байтов, прежде чем снова декодировать целое в Строку, иначе вы можете получить искаженные символы в точках разделения, когда символ занимает более 1 байта.

Если вы беспокоитесь об использовании памяти, когда строка может быть очень длинной, вам следует использовать streams (java.io package) для en / декодирования и разделения, чтобы избежать сохранения данных в памяти несколько раз в виде копий.В идеале вам вообще следует избегать использования исходной строки целиком и вместо этого использовать streams для чтения ее небольшими фрагментами, откуда бы вы ее ни взяли.

Другие советы

У вас есть два способа: быстрый и с сохранением памяти.Но сначала вам нужно знать, какие символы содержатся в Строке.ASCII?Существуют ли умляуты (символы от 128 до 255) или даже Юникод (s.getChar() возвращает что-то > 256).В зависимости от этого вам нужно будет использовать другую кодировку.Если у вас есть двоичные данные, попробуйте "iso-8859-1", потому что это сохранит данные в строке.Если у вас есть Unicode, попробуйте "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) { ... }

Для этого требуется примерно в три раза больше оперативной памяти, чем для исходной строки.

Более экономичный способ использования памяти - написать конвертер, который принимает StringReader и OutputStreamWriter (который оборачивает ByteArrayOutputStream).Копируйте байты из устройства чтения в устройство записи до тех пор, пока базовый буфер не будет содержать один фрагмент данных:

Когда это произойдет, скопируйте данные в реальный вывод (добавляя заголовок), скопируйте дополнительные байты (которые, возможно, были сгенерированы преобразованием Unicode-> byte) во временный буфер, вызовите buffer.reset() и запишите временный буфер в buffer.

Код выглядит примерно так (непроверенный):

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 ...

Для этого требуется всего пара килобайт оперативной памяти.

[РЕДАКТИРОВАТЬ] В комментариях было продолжительное обсуждение двоичных данных в строках.Прежде всего, совершенно безопасно помещать двоичные данные в строку, если вы будете осторожны при ее создании и хранении где-либо.Чтобы создать такую Строку, возьмите массив byte[] и:

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

В Java ISO-8859-1 (он же ISO-Latin1) представляет собой отображение 1: 1.Это означает, что байты в массиве никак не будут интерпретироваться.Теперь вы можете использовать substring() и тому подобное для данных или выполнять поиск по индексу, запускать для них регулярные выражения и т.д.Например, найдите позицию 0-байтового:

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

Это особенно полезно, если вы не знаете кодировку данных и хотите взглянуть на них, прежде чем какой-нибудь кодек испортит их.

Чтобы записать данные куда-нибудь, выполняется обратная операция:

байт[] данных = safe.getBytes("iso-8859-1");

Никогда не используйте методы по умолчанию new String(array) или String.getBytes()! Однажды ваш код будет выполнен на другой платформе, и он сломается.

Теперь проблема с символами > 255 в строке.Если вы используете этот метод, у вас никогда не будет такого символа в ваших строках.Тем не менее, если бы по какой-то причине они были, то getBytes() выдала бы исключение, потому что нет способа выразить все символы Unicode в ISO-Latin1, так что вы в безопасности в том смысле, что код не завершится без сбоев.

Кто-то может возразить, что это недостаточно безопасно и вам никогда не следует смешивать байты и строку.В наше время у нас нет такой роскоши.Многие данные не содержат явной информации о кодировке (файлы, например, не имеют атрибута "encoding" точно так же, как у них нет прав доступа или имени).XML - один из немногих форматов, который содержит явную информацию о кодировке, и существуют редакторы, такие как Emacs или jEdit, которые используют комментарии для указания этой важной информации.Это означает, что при обработке потоков байтов вы всегда должны знать, в какой кодировке они находятся.На данный момент невозможно написать код, который всегда будет работать, независимо от того, откуда поступают данные.

Даже при использовании XML вы должны прочитать заголовок файла в виде байтов, чтобы определить кодировку, прежде чем сможете декодировать содержимое.

Важный момент заключается в том, чтобы сесть и выяснить, какая кодировка была использована для генерации потока данных, который вы должны обработать.Если ты сделаешь это, ты хорош, если ты этого не сделаешь, ты обречен.Путаница возникает из-за того факта, что большинство людей не осознают, что один и тот же байт может означать разные вещи в зависимости от кодировки или даже что существует более одной кодировки.Кроме того, это помогло бы, если бы Sun не ввела понятие "кодировка платформы по умолчанию".

Важные моменты для начинающих:

  • Существует более одной кодировки (charset).
  • Здесь больше символов, чем используется в английском языке.Есть даже несколько наборы цифр (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

Я пробовал это для себя, мне нужно разбить огромную строку (почти 10 МБ) на 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 конструктор - это либо массив байтов, либо File или InputStream

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top