Apache Hive:如何使用Unicode字符(八进制大于177)作为字段分隔符



在我们的用例中,我们得到的UTF-8文本数据格式如下:

Data1§Data2
Data3§Data4

现在我们想有Data1和Data3在一列,Data2和Data4在Apache Hive的一列。听起来很简单。

但是,我们不能指定§字符(unicode U+00A7"Section Sign"见这里)作为字段分隔符。

我们试了以下几种方法,结果都不好。

1)使用以方法

终止的正常字段
ROW FORMAT DELIMITED FIELDS TERMINATED BY '§'

返回(注意?附加到每个单元格,在其他客户端中,unicode符号表示无法识别的符号)

+--------------------+--------------------+--+
| test.column1       | test.column2          |
+--------------------+--------------------+--+
| Data1?             | Data2?                |
| Data3?             | Data4?                |
+--------------------+--------------------+-

或八进制表示

ROW FORMAT DELIMITED FIELDS TERMINATED BY '247'

ROW FORMAT DELIMITED FIELDS TERMINATED BY '304247'
返回:

+--------------------+--------------------+--+
| test.column1       | test.column2          |
+--------------------+--------------------+--+
| Data1?Data2        | NULL                  |
| Data3?Data4        | NULL                  |
+--------------------+--------------------+--+
2)使用RegexSerDe
ROW FORMAT SERDE 'org.apache.hadoop.hive.contrib.serde2.RegexSerDe'
WITH SERDEPROPERTIES (
"input.regex" = "^([^\]]+)\247([^\]]+)$")

(在一些测试源数据中更改字段分隔符,例如为/,并使用57(八进制为/)产生正确的结果,但更改源数据对我们来说是不可行的。)

ROW FORMAT SERDE 'org.apache.hadoop.hive.contrib.serde2.RegexSerDe'
WITH SERDEPROPERTIES (
"input.regex" = "^([^\]]+)\$([^\]]+)$")

(在一个描述格式的表语句中,这会产生:

input.regex  ^([^\]]+)\uFFFDuFFFD([^\]]+)$

其中uFFFD是无法识别的符号的unicode表示)

SELECT的结果总是相同的:

+--------------------+--------------------+--+
| test.column1       | test.column2          |
+--------------------+--------------------+--+
| NULL               | NULL                  |
| NULL               | NULL                  |
+--------------------+--------------------+--+

我目前的研究结果如下:

1) Hive不能使用八进制数大于177的不可打印ASCII字符。让我惊讶的是,在github上的一些其他代码中,它说:

Hive可以以'ooo'的形式指定分隔符,ooo在其中在000到177之间的三位八进制数。

2)我也发现了证据,只有一个字节的字符可以用作BigSQL文档中的字段分隔符(但不是在官方文档中),它说:

分隔符必须是单字节字符

和我的研究§(unicode U+00A7)是一个2字节字符(11000010:10100111)

这是否意味着我不能使用这个分隔符或者有其他方法可以使用它?

小更新,如果这个问题没有解决,有人需要它:

我尝试了以下方法,将数据暂存为单列表,然后将§转换为,(逗号),然后用逗号分隔它。这适用于小样本数据,但不适用于包含200多个列的大型生产表,并出现错误。

select
split(a.textcolumn, '\,')[0] as column1
,split(a.textcolumn, '\,')[1] as column2
from
(select translate(textcolumn, '§', ',') as textcolumn from database.stage) a;

错误提示:

SQL Error: java.io.IOException: org.apache.hadoop.hive.ql.metadata.HiveException: Error evaluation translate(stagingstring, '§',';')java.io.IOException: org.apache.hadoop.hive.ql.metadata.HiveException:计算translate(stagingstring, '§',';')时出错java.io.IOException: org.apache.hadoop.hive.ql.metadata.HiveException:计算translate(stagingstring, '§',';')时出错org.apache.hadoop.hive.ql.metadata.HiveException:计算translate(stagingstring, '§',';')出错org.apache.hadoop.hive.ql.metadata。翻译错误(stagingstring, '§',';')java.lang.IllegalArgumentException:空

更新2:

上面的方法可以工作,但是如果源数据不干净(有其他UTF-8问题),它将抛出上述错误。

您需要使用

创建您的表

Fields Terminated by '-89'

"section符号"的十进制代码为167。

167 - 256 = -89

这个-89应该是你的分隔符。Hive允许的分隔符范围是-127到127。

要进一步阅读以下来自Cloudera的代码片段。

https://www.cloudera.com/documentation/enterprise/5-8-x/topics/impala_create_table.html

注意:CREATE TABLE子句FIELDS TERMINATED BY、ESCAPED BY和LINES TERMINATED BY对其参数使用的字符串字面值有特殊的规则,因为它们都需要一个字符。您可以使用被单引号或双引号包围的常规字符,八进制序列,如'54'(表示逗号),或'-127'范围内的整数。'128'(带引号但没有反斜杠),它被解释为单字节ASCII字符。256减去负值;例如,FIELDS以'-2'终止将字段分隔符设置为ASCII码254,即某些数据格式使用的"冰岛荆棘"字符作为分隔符。

我知道这是一个冷案例,但这个问题可以使用MultiDelimitSerDe来解决。

实际上,Hive默认的SerDe(称为LazySimpleSerDe)只支持单字节分隔符。正如alpcoder所说,这是没有记录的。你需要阅读源代码来计算它。

MultiDelimitSerDe被设计为支持多字符分隔符。幸运的是,它支持任何多字节字符。

答案在我对上面问题的更新中。但是,如果数据不干净或包含我发现的其他有问题的字符,这将抛出和错误。

最新更新