Извлечь первую действительную строку строки из байтового массива - PullRequest
1 голос
/ 12 октября 2009

Я пишу утилиту на Java, которая читает поток, который может содержать как текстовые, так и двоичные данные. Я хочу избежать ожидания ввода-вывода. Для этого я создаю поток, который продолжает читать данные (и ждет их), помещая их в буфер, чтобы клиенты могли проверить доступность и прекратить ожидание в любое время (закрыв входной поток, который сгенерирует IOException и прекратит ожидание). ). Это работает очень хорошо, поскольку чтение байтов из него; что касается двоичного кода.

Теперь я также хочу, чтобы клиент мог легко читать строки из него, например '.hasNextLine()' и '.readLine()'. Без использования потока ожидания ввода-вывода, такого как буферизованный поток, ( Q1 ). Как я могу проверить, содержит ли двоичный файл (byte []) допустимую строку Unicode (в форме длины первой строки) )? Я смотрю на API String / CharSet, но не могу его найти (или мне его не хватает?). (ПРИМЕЧАНИЕ. Если возможно, я не хочу использовать не встроенную библиотеку).

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

1). Я смотрю с начала байтового массива, пока не найду '\ n' или '\ r' без '\ n'. 2). Затем я вырезал байтовый массив от начала до этой точки и использовал его для создания строки (с CharSet, если указан), используя 'new String(byte[])' или 'new String(byte[], CharSet)'. 3). Если этот успех без исключения, мы нашли первую правильную строку и вернем ее. 4). В противном случае эти байты могут не быть строкой, поэтому я смотрю дальше на другой '\ n' или '\ r' w / o '\ n'. и этот процесс повторить. 5. Если поиск заканчивается в конце доступных байтов, я останавливаюсь и возвращаю ноль (допустимая строка не найдена).

У меня вопрос ( Q2 ). Адекватен ли следующий алгоритм?

Как раз в то время, когда я собирался реализовать это, я искал в Google и обнаружил, что есть много других кодов для новой строки, например U + 2424 , U + 0085, U + 000C , U + 2028 и U + 2029 .

Итак, мой последний вопрос ( Q3 ), действительно ли мне нужно обнаружить этот код? Если я это сделаю, увеличит ли это вероятность ложной тревоги?

Мне хорошо известно, что распознавать что-то из двоичных файлов не является абсолютным. Я просто пытаюсь найти лучший баланс.

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

Заранее всем спасибо.

Ответы [ 6 ]

4 голосов
/ 12 октября 2009

Боюсь, что ваша проблема не вполне определена. Вы пишете, что хотите извлечь «первую правильную строку строки» из ваших данных. Но является ли последовательность байтов "допустимой строкой", зависит от кодировки. Таким образом, вы должны решить, какую кодировку (ы) вы хотите использовать в тестировании.

Разумный выбор будет:

  • кодировка платформы по умолчанию (свойство Java "file.encoding")
  • UTF-8 (как это чаще всего встречается)
  • список кодировок, которые, как вы знаете, будут использовать ваши клиенты (например, несколько русских или китайских кодировок)

То, что имеет смысл, будет зависеть от данных, общего ответа нет.

После того, как у вас есть кодировки, должна возникать проблема завершения строки, так как большинство кодировок имеют правила относительно того, что завершает строку. В ASCII или Latin-1 достаточно LF, CR-LF и LF-CR. В Юникоде вам нужны все те, что вы перечислили выше.

Но, опять же, нет общего ответа, так как коды новых строк строго не регламентированы. Опять же, это будет зависеть от ваших данных.

2 голосов
/ 12 октября 2009

Прежде всего позвольте мне задать вам вопрос, данные, которые вы пытаетесь обработать устаревшими данными? Другими словами, вы несете ответственность за формат входного потока, который вы пытаетесь использовать здесь?

Если вы действительно контролируете формат ввода, то вы, вероятно, хотите принять решение Binary vs. Text из алгоритма Q1. Для меня этот алгоритм имеет одну неприятную часть.

    `4). Otherwise, these bytes may not be a string, so I look further to 
another '\n' or '\r' w/o '\n'. and this process repeat.`

Вы отказываетесь от ввода перед разделителем строки и берете байты, начинающиеся сразу после, или пытаетесь переоценить строку с двумя разделителями строки? Если первое, возможно, у вас сломан интерфейс двоичных данных, а во втором случае вы все еще не можете правильно проанализировать текст.

Я думаю, что наличие четко определенных маркеров для двоичных и текстовых данных в вашем потоке значительно упростит ваш алгоритм.

Пара слов о конструкторе строк. new String(byte[], CharSet) не будет генерировать никаких исключений, если байтовый массив не относится к конкретному CharSet, вместо этого он создаст строку, полную вопросительных знаков (возможно, не того, что вы хотите). Если вы хотите создать исключение, вы должны использовать CharsetDecoder .

Также обратите внимание, что в Java 6 есть 2 конструктора, которые принимают кодировку String(byte[] bytes, String charsetName) и String(byte[] bytes, Charset charset). Некоторое время назад я провел простой тест производительности, и конструктор с String charsetName на величины быстрее, чем тот, который принимает объект Charset (Вопрос Солнцу: ошибка, особенность?).

1 голос
/ 20 октября 2009

Я только что решил это, чтобы заставить тестовую заглушку работать на дейтаграмме - я сделал byte [] varName = String.getBytes (); затем final int len ​​= varName.length; затем отправьте int как DataOutputStream, а затем байтовый массив и просто выполните readInt () в rcv, затем прочитайте байты (число) с использованием readInt.

Не библиотека, и не сложно. Просто прочитайте readUTF и сделайте то, что они сделали для байтов.

Строка должна создаваться из байтового массива, восстановленного таким образом, если нет других проблем. Если строку можно восстановить, она может быть помещена в буфер ... нет?

Может быть, можно просто использовать чтение / запись UTF () в DataStream - почему бы и нет?

{edit: по запросу OP}

//Sending end 

String data = new String("fdsfjal;sajssaafe8e88e88aa");// fingers pounding keyboard
DataOutputStream dataOutputStream = new DataOutputStream();//
final Integer length = new Integer(data.length());
dataOutputStream.writeInt(length.intValue());//
dataOutputStream.write(data.getBytes());//
dataOutputStream.flush();//
dataOutputStream.close();//

// rcv end

DataInputStream dataInputStream = new DataInputStream(source);
final int sizeToRead = dataInputStream.readInt();
byte[] datasink = new byte[sizeToRead.intValue()];
dataInputStream.read(datasink,sizeToRead);
dataInputStream.close;
try
{
   // constructor
   // String(byte[] bytes, int offset, int length)

   final String result = new String(datasink,0x00000000,sizeToRead);//          

   // continue coding here

Сделай одолжение, держи меня подальше. Это очень быстро, прямо в инструменте публикации - код, вероятно, содержит существенные ошибки - мне быстрее объяснить это при написании Java. Будут другие, которые могут перевести его на другой язык (и) кода, который вы тоже можете использовать, если хотите. в другой кодовой базе. Вам понадобится перехват исключений и так далее, просто выполните компиляцию и начните исправлять ошибки. Когда вы получите чистую компиляцию, начните все сначала и ищите грубые ошибки. (это то, что в инженерии называется промах - промах)

1 голос
/ 19 октября 2009

Я бы попробовал это:

  • заставить читателя ввода-вывода помещать строки / строки в потокобезопасную коллекцию (например, некоторую реализацию BlockingQueue)
  • основной код имеет ссылку только на синхронизированную коллекцию и при необходимости проверяет наличие новых данных, например queue.peek (). Ему не нужно знать ни о потоке ввода-вывода, ни о потоке.

Некоторые псевдо-Java-код (пропущено исключение и обработка io, обобщенные элементы, импорт ++):

class IORunner extends Thread {
  IORunner(InputStream in, BlockingQueue outputQueue) {
    this.reader = new BufferedReader(new InputStreamReader(in, "utf-8"));
    this.outputQueue = outputQueue;
  }

  public void run() {
    String line;
    while((line=reader.readLine())!=null)
      this.outputQueue.put(line);
  }
}

class Main {
  public static void main(String args[]) {
    ...
    BlockingQueue dataQueue = new LinkedBlockingQueue();
    new IORunner(myStreamFromSomewhere, dataQueue).start();

    while(true) {
      if(!dataQueue.isEmpty()) { // can also use .peek() != null
        System.out.println(dataQueue.take());
      }
      Thread.sleep(1000);
    }
  }
}
  • Коллекция отделяет вход (поток) больше от основного кода. Вы также можете ограничить количество используемых строк / mem, создав очередь с ограниченной емкостью (см. Blockingqueue doc).
  • BufferedReader обрабатывает проверку новых строк для вас :) InputStreamReader обрабатывает кодировку (рекомендуем установить ее самостоятельно, так как значение по умолчанию изменяется в зависимости от ОС и т. Д.).
1 голос
/ 12 октября 2009

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

Q1: Не могу придумать что-то лучше, чем алгоритм, который вы используете

В3: Полагаю, этого будет достаточно для проверки \ r и \ n. Другие слишком экзотичны для обычных текстовых файлов.

1 голос
/ 12 октября 2009

Пространство имен java.text предназначено для такого рода операций на естественном языке. Статический метод BreakIterator.getLineInstance() возвращает итератор, который обнаруживает разрывы строк. Вам все же нужно знать локаль и кодировку для достижения наилучших результатов.

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