从字节数组中提取第一个有效的字符串行

时间:2009-10-12 07:12:17

标签: java unicode

我正在用Java编写一个实用程序来读取可能包含文本和二进制数据的流。我想避免让I / O等待。为此,我创建了一个线程来继续读取数据(并等待它)将其放入缓冲区,这样客户端就可以检查可用性并在需要时终止等待(通过关闭将生成IOException并停止等待的输入流) )。就读取字节而言,这很好用;就二元而言。

现在,我还希望客户能够轻松阅读'.hasNextLine()''.readLine()'。不使用像缓冲流那样的I / O等待流,( Q1 )如何检查二进制(byte [])是否包含有效的unicode行(以第一行的长度形式)线)?我环顾一下String / CharSet API但找不到它(或者我想念它?)。 (注意:如果可能,我不想使用非内置库。)

由于我找不到,我尝试创建一个。没有这么复杂,这是我的算法。

1)。我从字节数组的开头看,直到找到没有'\ n'的'\ n'或'\ _ \'。 2)。然后,我从开始到该点剪切字节数组,并使用它来创建一个字符串(如果指定了CharSet),使用'new String(byte[])''new String(byte[], CharSet)'。 3)。如果成功无一例外,我们找到第一个有效行并返回它。 4)。否则,这些字节可能不是字符串,所以我进一步观察另一个'\ n'或'\ r'w / o'\ n'。这个过程重复一遍。 5.如果搜索在可用字节的末尾结束,我将停止并返回null(未找到有效行)。

我的问题是( Q2 )以下算法是否足够?

就在我即将实施的时候,我在Google上进行了搜索,发现还有很多新代码的代码,例如U+2424U+0085, U+000C, U+2028 and U+2029

所以我的最后一个问题是( Q3 ),我真的需要检测这些代码吗?如果我这样做,是否会增加误报的可能性?

我很清楚从二进制中识别某些东西并不是绝对的。我只是想找到最佳平衡点。

总结一下,我有一个byte数组,我想从中提取第一个有效的字符串行,有/没有特定的CharSet。这必须用Java完成,并避免使用任何非内置库。

提前谢谢大家。

6 个答案:

答案 0 :(得分:4)

我担心你的问题没有明确定义。您写道要从数据中提取“第一个有效的字符串行”。但是,某些字节序列是否是“有效字符串”取决于编码。因此,您必须确定要在测试中使用哪种编码。

明智的选择是:

  • 平台默认编码(Java属性“file.encoding”)
  • UTF-8(最常见)
  • 您知道客户将使用的编码列表(例如几种俄语或中文编码)

有意义取决于数据,没有一般答案。

一旦进行了编码,就会出现线路终止的问题,因为大多数编码都有关于终止线路的规则。在ASCII或Latin-1中,LF,CR-LF和LF-CR就足够了。在Unicode上,您需要上面列出的所有内容。

但是,由于新的线路代码没有受到严格监管,因此没有一般性的答案。同样,这取决于您的数据。

答案 1 :(得分:2)

首先让我问你一个问题,你试图处理遗留数据的数据是什么?换句话说,您是否负责您在此处尝试使用的输入流格式?

如果你确实在控制输入格式,那么你可能想要从Q1算法中选择Binary vs. Text。对我来说,这个算法有一个令人不安的部分。

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

您是否在行终止符之前解除输入并获取紧接在之后开始的字节,或尝试使用现在的2行终止符重新评估字符串?如果是前者,您可能已经破坏了二进制数据接口,如果是后者,您可能仍然无法正确解析文本。

我认为在流中为二进制数据和文本数据定义明确的标记会大大简化您的算法。

String构造函数上的几个单词。如果字节数组不是特定的CharSet,new String(byte[], CharSet)将不会生成任何异常,而是会创建一个充满问号的字符串(可能不是您想要的)。如果要生成例外,则应使用CharsetDecoder

另请注意,在Java 6中有2个构造函数可以使用charset String(byte[] bytes, String charsetName)String(byte[] bytes, Charset charset)。我前段时间进行了一些简单的性能测试,使用String charsetName的构造函数比采用Charset对象的构造函数更快(问题解释为Sun:bug,feature?)。

答案 2 :(得分:1)

java.text命名空间是为这种自然语言操作而设计的。 BreakIterator.getLineInstance()静态方法返回一个检测换行符的迭代器。但是,您确实需要知道区域设置和编码以获得最佳结果。

答案 3 :(得分:1)

Q2:您使用的方法似乎足够合理。

Q1:无法想到比你正在使用的算法更好的东西

问题3:我相信它足以测试\ r和\ n。其他对于通常的文本文件来说太奇特了。

答案 4 :(得分:1)

我会试试这个:

  • 让IO读者将字符串/行放入线程安全集合中(例如BlockingQueue的一些实现)
  • 主代码仅引用同步集合并在需要时检查新数据,例如queue.peek()。它不需要知道io线程和流。

一些伪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处理字符集(建议您自己设置一个字符集,因为默认值会根据操作系统等而改变)。

答案 5 :(得分:1)

我刚刚解决了这个问题,让测试存根工作于Datagram - 我做了byte [] varName = String.getBytes();然后是最后的int len = varName.length;然后将int作为DataOutputStream然后发送到字节数组,然后在rcv上执行readInt(),然后使用readInt读取字节数(count)。

不是lib,也不难做到。只需阅读readUTF并做他们为字节做的事情。

字符串应该从以这种方式恢复的字节数组构建,如果没有,你还有其他问题。如果字符串可以重建,它可以被缓冲......不是吗?

可能只能在DataStream中使用读/写UTF() - 为什么不呢?

{编辑:根据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会更快〜会有其他人可以将其翻译成其他代码语言,如果你愿意的话也可以在另一个代码库中。您将需要异常捕获等,只需进行编译并开始修复错误。当你得到一个干净的编译时,从头开始重新开始并寻找错误。 (这就是在工程中被称为大错的错误)