Hadoop MapReduce中Text.set(string)赋值异常的问题排查
Hadoop Map函数字符串分割异常排查
问题背景
Map函数处理的键值对格式如下(键值具体内容不固定):
key: "somevalue" value: "value \t comma separated values"
示例数据:
key:"0" value:"5\t1,2,3,4"
正常输出场景
执行以下代码时,输出符合预期:
Text debug; for (Text val : values) { String[] segments = val.toString().split("\t"); debug = new Text(); debug.set(val.toString()); context.write(key, debug); }
输出示例:
key value 0 8 1,2,4,5 0 2 0,4,5
异常场景1:含try-catch的代码输出异常
替换为以下代码后,输出仅显示键和制表符:
Text debug; for (Text val : values) { String[] segments = val.toString().split("\t"); debug = new Text(); if(val.toString().split("\t").length > 1) { try{ debug.set(val.toString().split("\t")[1]); }catch(Exception e) { debug.set("Exception"); } } context.write(key, debug); }
预期输出:
key second part of value (after \t) 1 2,3,4,5,6 1 4,5,6,6,7
实际输出:
key Tab (tab character after key) 1TAB 1TAB ... 2TAB
异常场景2:替换为if-else后的输出异常
将代码改为if-else结构后,所有输出均为only one:
Text debug; for (Text val : values) { String[] segments = val.toString().split("\t"); debug = new Text(); if(val.toString().split("\t").length > 1) { debug.set(val.toString().split("\t")[1]); } else { debug.set("only one"); } context.write(key, debug); }
输出示例:
0 only one ... 100 only one
本地测试对比
本地Java环境中,执行"1\t2".split("\t")可得到数组["1", "2"],分割行为正常。
核心原因分析
你的代码中val.toString().split("\t")始终返回长度为1的数组,主要有两种可能:
val中无实际制表符,仅含字面量\t
你示例中的value:"5\t1,2,3,4"如果是数据的字面存储(即字符串包含\和t两个字符,而非ASCII制表符0x09),那么split("\t")(匹配制表符)自然无法分割,数组长度始终为1。本地测试用的是Java字符串中的制表符(\t会被解析为制表符),但Map函数中的val实际存储的是"5\\t1,2,3,4",两者本质不同。制表符位于字符串末尾,split默认丢弃末尾空串
如果val的格式是"value\t"(制表符在最后),String.split("\t")默认会丢弃分割后的末尾空字符串,返回数组长度为1。此时需要用split("\t", -1)强制保留所有空字符串,分割后数组长度会变为2(比如"8\t".split("\t", -1)会得到["8", ""])。
验证与解决方案
- 验证
val实际内容:在正常输出代码中,将debug.set(val.toString())改为debug.set("DEBUG: " + val.toString().replace("\t", "[TAB]")),可直观看到val中的制表符是实际字符还是字面量。 - 字面量
\t问题:将分割参数改为split("\\\\t")(正则中\需转义,Java字符串需写4个反斜杠),匹配字面量的\t组合。 - 末尾制表符问题:使用
split("\t", -1)替代split("\t"),强制保留所有分割结果。
内容的提问来源于stack exchange,提问作者qwerty_99
相关产品推荐
相关产品推荐

