【问题标题】:Parse pipe seperated string (Pipes can be escaped ) in Java在 Java 中解析管道分隔的字符串(管道可以转义)
【发布时间】:2023-04-07 05:27:01
【问题描述】:

接收一个管道分隔的参数,需要对其进行分词;但是可以使用 \| 转义管道表示它不是分隔符,而是令牌的一部分。我知道 Java 编译器使用字符串文字中的 " 和转义引号 \" 来执行此操作。

但不确定一个好的实用函数/算法来标记这样的字符串。输出将是一个字符串列表。

示例输入 1:"jk|g4",输出:"jk" , "g4"(包含 2 个字符串的列表)

示例输入 2:"j\|k|g4|b",输出:"j|k" , "g4", "b"(包含 3 个字符串和文字管道的列表)

尝试使用String.indexOf("|"),然后检查前一个字符是否为\ ...但有错误并想确定:有没有更简单的方法?正则表达式?

这是我所拥有的:

import java.util.ArrayList;
import java.util.List;

public class PasrePipes {
    public static void main(String[] args) {

        String in = "j\\|k|g4|b";
        {
            String[] ex = { "j|k", "g4", "b" };
            tst(in, ex);
        }

        in = "j|k|g4|b";
        {
            String[] ex = { "j", "k", "g4", "b" };
            tst(in, ex);
        }
    }

    private static void tst(String in, String[] ex) {
        System.out.println("----\n" + in);
        List<String> toks = parse(in);
        if (toks.size() != ex.length) {
            System.out.println("size mismatch, got :" + toks.size() + " exp " + ex.length);
        }
        for (int i = 0; i < ex.length; i++) {
            if (toks.size() > i) {
                String n = toks.get(i);
                if (!ex[i].equals(n)) {
                    System.out.println(" mismatch :" + i + ", got :" + n + "; exp :" + ex[i]);
                } else {
                    System.out.println(" okay :" + i + "; exp :" + ex[i]);
                }
            }
        }

        System.out.println("--");
    }

    private static List<String> parse(String in) {
        List<String> tokens = new ArrayList<String>();
        int i = in.indexOf('|');
        int old = 0;

        while (i > -1) {
            if (i > 0) {
                if (in.charAt(i - 1) == '\\') {
                    i = in.indexOf('|', i + 1);
                    continue;
                }
            }
            String s = in.substring(old, i);
            s.replace("\\|", "|");
            tokens.add(s);
            old = i + 1;
            i = in.indexOf('|', i + 1);

        }
        if(i > 0 && i < (in.length() - 1)) {
            String s = in.substring(i + 1);
            s.replace("\\|", "|");
            tokens.add(s);
        }
        return tokens;

    }
}

【问题讨论】:

  • 请显示您尝试过的代码以及您遇到的错误。
  • @tgkprog:我假设您实际上是指"j\\|k|g4|b",因为"j\|k|g4|b" 在java 中不是有效的字符串文字。如果我错了,请纠正我。
  • 添加了我的@jim-garrison
  • 是的,我正在从表单中获取输入,所以将您看到的内容放在文本框中...但是在代码中需要转义 \@Syon

标签: java regex parsing escaping tokenize


【解决方案1】:

没有办法用单个正则表达式语句同时进行拆分和替换。但是你可以用消极的眼神来分裂:

(?<!\\)[\|]

然后将\|替换为|

String value = "j\\|k|g4|b";
String[] split = value.split("(?<!\\\\)(\\|)");
for(int i = 0; i < split.length; i++){
    split[i] = split[i].replaceAll("(\\\\\\|)", "\\|");
    System.out.println(split[i]);
}

输出:

j|k
g4
b

更新

请注意,如果您直接在管道前转义斜线,这将不起作用。

String value = "j\\\\|k|g4|b";
...

输出:

j\|k
g4
b

期望的输出:

j\
k
g4
b

因为 Java 不支持变长后视,所以无法在分隔符处拆分字符串。但是,您可以使用 PatternMatcher 来匹配分隔符前带有偶数个斜杠的每个字段。

String value = "j\\|k|g4|b|kjbk\\\\\\|\\ml|jbkjbjk\\\\\\\\|k\\jb\\k\\\\\\j|m\\\\\\|\\\\kb";
Pattern pattern = Pattern.compile("(([^\\\\](\\\\\\\\)*\\\\\\|)|([^\\|]))+");
Matcher matcher = pattern.matcher(value);
List<String> fields = new ArrayList<String>();
while(matcher.find()){
    String field = matcher.group().replaceAll("(\\\\\\|)", "\\|");
    fields.add(field);
    System.out.println(field);
}

输出:

j|k
g4
b
kjbk\\|\ml
jbkjbjk\\\\
k\jb\k\\\j
m\\|\\kb

【讨论】:

  • @tgkprog:你确定吗?适用于我的输入。你能指出一个产生同样失败的在线正则表达式工具吗?
  • 使用 java 7 复制了您的代码,但不仅仅是打印添加到循环中的列表中:tokens.add(split[i]);我不认为在线工具在内部使用 java。可能是一个 java 错误 :( 并且适用于 python 或 php
  • 它有效,是的,我忘了改变价值 :) 呵呵,我赞成你的答案,但由于两项工作都接受了分数较少的一项
  • @tgkprog,您可以找到几乎所有正则表达式风格的在线测试人员。对于 Java 正则表达式,我通常使用 RegexPlanet
  • @tgkprog:忘记了你可能在管道前直接转义斜线的情况。也为此添加了解决方案。
【解决方案2】:

如果速度不是大问题,您可以尝试以下 pidgin 方式:

//replaces all escaped pipes to a temporary replacement
string = string.replaceAll("\\\\\\|", "tmpReplacement");

//iterate through all parts of the string which is divided by a pipe
for(String str : string.split("\\|"))
{
     str = str.replaceAll("tmpReplacement" , "\\|"/*or how you need it*/);
     //now you can use str; str is each part of the variable string, which has not been escaped
}

【讨论】:

  • tmpReplacement 是输入?>
  • 不,输入是变量字符串。 “tmpReplacement”只是一个用于忽略转义管道的字符串文字。后来他们又被正确的取代了。
  • 谢谢@user2088127 :-) 将进行更多测试(适用于我拥有的两个样本)
  • 没问题,如果您的测试失败,请询问。如果他们没有失败,我将不胜感激 ^^
  • 非常感谢 :) 我是新来的,需要一些声誉,抱歉 :)
【解决方案3】:

这可以通过正则表达式来完成(正如 Syon 演示的那样),但它看起来太复杂且容易出错。我发现这个简单的函数是更好的解决方案:

/*
 * Parses a delimited string with an escape character 
 */
public static List<String> parse(String s, char delimeter, char escape){
    List<String> result = new ArrayList<String>();

    StringBuilder sb = new StringBuilder();
    boolean escaped = false;
    for(char ch : s.toCharArray()){
        if(escaped){ 
            sb.append(ch);
            escaped = false;
        }
        else {
            if (ch == escape){
                escaped = true;
            }
            else if(ch == delimeter){
                result.add(sb.toString());
                sb.setLength(0);
            }
            else{
                sb.append(ch);
            }
        }
    }

    result.add(sb.toString());

    return result;
}

你这样称呼它:

parse("j\\|k|g4|b", '|', '\\')    // --> [ "j|k", "g4", "b" ]

【讨论】:

  • 性能是最佳的,因为每个字符只被访问一次,你必须访问所有字符 - 它与任何正则表达式解决方案相同或更好
  • 太好了,谢谢我接受了另一个人的回答,但这看起来也很棒。 +1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-29
  • 2019-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多