2017-05-18 80 views
2

提取数据我从OkHttpClient类似这样的回复:快速的方法来从字符串

{"CUSTOMER_ID":"928941293291"} 
{"CUSTOMER_ID":"291389218398"} 
{"CUSTOMER_ID":"1C4DC4FC-02Q9-4130-S12B-762D97FS43C"} 
{"CUSTOMER_ID":"219382198"} 
{"CUSTOMER_ID":"282828"} 
{"CUSTOMER_ID":"21268239813"} 
{"CUSTOMER_ID":"1114445184"} 
{"CUSTOMER_ID":"2222222222"} 
{"CUSTOMER_ID":"99218492183921"} 

我想提取所有的客户ID是Long类型的(然后跳到1C4DC4FC-02Q9-4130-S12B-762D97FS43C)在minId和maxId之间。 这是我实现:

final List<String> customerIds = Arrays.asList(response.body().string() 
        .replace("CUSTOMER_ID", "") 
        .replace("\"", "") 
        .replace("{", "").replace(":", "") 
        .replace("}", ",").split("\\s*,\\s*")); 
for (final String id : customerIds) { 
    try { 
     final Long idParsed = Long.valueOf(id); 
     if (idParsed > minId && idParsed < maxId) { 
      ids.add(idParsed); 
     } 
    } catch (final NumberFormatException e) { 
     logger.debug("NumberFormatException", e); 
    } 
} 

我有客户ID(约1M)的一个长长的清单,然后性能是非常重要的。这是我的行为的最佳实施?

+1

是不知何故json,你可以解析?? –

+1

我想我在那里看到UUID,做min和max甚至在这里有意义吗? –

+0

@ΦXocę웃Пepeúpaツ我的例子中报等于我的使用情况(仅针对multipled 1M行) –

回答

1

既然你有一个大文件,那么逐行阅读内容是一种可行的方式,并且不要替换CUSTOMER_ID,而是定义一个更好的正则表达式模式。

按照你的方法:更换USER_ID和使用正则表达式:

String x = "{\"CUSTOMER_ID\":\"928941293291\"}{\"CUSTOMER_ID\":\"291389218398\"}{\"CUSTOMER_ID\":\"1C4DC4FC-02Q9-4130-S12B-762D97FS43C\"}" 
      + "{\"CUSTOMER_ID\":\"99218492183921\"}"; 

x = x.replaceAll("\"CUSTOMER_ID\"", ""); 
Pattern p = Pattern.compile("\"([^\"]*)\""); 
Matcher m = p.matcher(x); 
while (m.find()) { 
    System.out.println(m.group(1)); 
} 

或实现匹配所有之间正则表达式: “”}

String x = "{\"CUSTOMER_ID\":\"928941293291\"}{\"CUSTOMER_ID\":\"291389218398\"}{\"CUSTOMER_ID\":\"1C4DC4FC-02Q9-4130-S12B-762D97FS43C\"}" 
      + "{\"CUSTOMER_ID\":\"99218492183921\"}"; 

Pattern p = Pattern.compile(":\"([^\"]*)\"}"); 
Matcher m = p.matcher(x); 
while (m.find()) { 
    System.out.println(m.group(1)); 
} 

所以无需更换CUSTOMER_ID

+0

对于1点M的记录使用正则表达式来打!?你需要64GB的RAM或类似的东西 –

+0

喔!!我搞砸通过线的那部分 –

+0

然后读线可以是一个办法 –

0

可以忽略所有非数值型字段

long[] ids = 
    Stream.of(response.body().string().split("\"")) 
      .mapToLong(s -> parseLong(s)) 
      .filter(l -> l > minId && i < maxId) 
      .toArray(); 

static long parseLong(String s) { 
    try { 
     if (!s.isEmpty() && Character.isDigit(s.charAt(0))) 
      return Long.parseLong(s); 
    } catch (NumberFormatException expected) { 
    } 
    return Long.MIN_VALUE 
} 

或者,如果你使用的是Java 7

List<Long> ids = new ArrayList<>(); 
for (String s : response.body().string().split("\"")) { 
    long id = parseLong(s); 
    if (id > minId && id < maxId) 
     ids.add(id); 
} 
+0

不使用Java 8 –

+1

@LuigiSaggese加入如何7. –

0

您可以使用Files.lines()从您的文件流数据。在这里,我演示了从List使用stream

List<String> sample = Arrays.asList(
     "{\"CUSTOMER_ID\":\"928941293291\"}", 
     "{\"CUSTOMER_ID\":\"291389218398\"}", 
     "{\"CUSTOMER_ID\":\"1C4DC4FC-02Q9-4130-S12B-762D97FS43C\"}", 
     "{\"CUSTOMER_ID\":\"219382198\"}", 
     "{\"CUSTOMER_ID\":\"282828\"}", 
     "{\"CUSTOMER_ID\":\"21268239813\"}", 
     "{\"CUSTOMER_ID\":\"1114445184\"}", 
     "{\"CUSTOMER_ID\":\"2222222222\"}", 
     "{\"CUSTOMER_ID\":\"99218492183921\"}" 
); 

static final long MIN_ID = 1000000L; 
static final long MAX_ID = 1000000000000000000L; 

public void test() { 
    sample.stream() 
      // Extract CustomerID 
      .map(s -> s.substring("{\"CUSTOMER_ID\":\"".length(), s.length() - 2)) 
      // Remove any bad ones - such as UUID. 
      .filter(s -> s.matches("[0-9]+")) 
      // Convert to long - assumes no number too big, add a further filter for that. 
      .map(s -> Long.valueOf(s)) 
      // Apply limits. 
      .filter(l -> MIN_ID <= l && l <= MAX_ID) 
      // For now - just print them. 
      .forEach(s -> System.out.println(s)); 
} 
+0

我的应用程序不能使用Java 8做同样在Java中我的应用程序 –

1

尽量避免例外!当10%-20%的数字解析失败时,它需要10倍的时间来执行,并且它(你可以为它编写一个litte测试)。

如果输入酷似你显示它,你应该使用廉价的操作: 通过线与BufferedReader线读取文件(就像前面提到过)或者(如果你有整个数据串)我们StringTokenizer处理每行分隔。 每行以{"CUSTOMER_ID":"开头,以"}结尾。不要使用replace或正则表达式(更糟糕的是)删除它!只需使用一个简单的substring

String input = line.substring(16, line.length() - 2) 

为避免你需要找到度量ID和UUID(区分异常?),所以你的解析工作无一例外。例如,您的ID将是正位,但您的UUID包含减号,或者long只能包含20位数字,但您的UUID包含35个字符。所以这是一个简单的if-else而不是try-catch。

对于那些认为在解析数字时不能捕获NumberFormatException的人:如果存在无法解析的id,则整个文件已损坏,这意味着您不应该尝试继续,但会失败。


这是一个小测试,看看捕获异常和测试输入之间的性能差异:

long REPEATS = 1_000_000, startTime; 
final String[] inputs = new String[]{"0", "1", "42", "84", "168", "336", "672", "a-b", "1-2"}; 
for (int r = 0; r < 1000; r++) { 
    startTime = System.currentTimeMillis(); 
    for (int i = 0; i < REPEATS; i++) { 
     try { 
      Integer.parseInt(inputs[i % inputs.length]); 
     } catch (NumberFormatException e) { /* ignore */ } 
    } 
    System.out.println("Try: " + (System.currentTimeMillis() - startTime) + " ms"); 
    startTime = System.currentTimeMillis(); 
    for (int i = 0; i < REPEATS; i++) { 
     final String input = inputs[i % inputs.length]; 
     if (input.indexOf('-') == -1) 
      Integer.parseInt(inputs[i % inputs.length]); 
    } 
    System.out.println("If: " + (System.currentTimeMillis() - startTime) + " ms"); 
} 

我的结果是:

  • 〜20毫秒(测试)和〜200毫秒(捕捉)有20%无效输入。
  • 〜22毫秒(测试)和〜130毫秒(捕捉)与10%无效输入。

由于JIT或其他优化,这些类型的性能测试很容易完成。但我认为你可以看到一个方向。

+0

你认为有更好的表现,以检查是否字符串包含特殊字符(例如“ - ” )比抛出NumberFormatException跳过无效的长? –

+0

是的!但这取决于你的输入。在这里,你期望无效的数字,你想滥用try-catch控制流。我对我的答案附了一个小测试。 – Obenland

+0

感谢您的澄清:) –

0

首先,你应该尝试逐行读取文件中的行。然后从每行你应该提取id如果它匹配的模式,并收集到一个数组。这里是用python实现的类似解决方案。

import re 
# Open the file 
with open('cids.json') as f: 
    # Read line by line 
    for line in f: 
     try: 
      # Try to extract matching id with regex pattern 
      _id = re.search('^{[\w\W]+:"([A-Z\d]+-[A-Z\d]+-[A-Z\d]+-[A-Z\d]+-[A-Z\d]+)"}', line).group(1) 
      customer_ids.append(_id) 
     except: 
      print('No match')