【问题标题】:Tokenize a string with a space in java在java中用空格标记字符串
【发布时间】:2009-10-01 00:21:45
【问题描述】:

我想标记这样的字符串

String line = "a=b c='123 456' d=777 e='uij yyy'";

我不能这样拆分

String [] words = line.split(" ");

知道如何拆分以便获得类似的令牌

a=b
c='123 456'
d=777
e='uij yyy';  

【问题讨论】:

  • 难道你不能只使用正则表达式按空格分隔,除非你在引号内(不是我知道正则表达式,但我很确定你可以这样做)。
  • 您的代码使用 jdk 1.6.0_13 在这里完美运行
  • @LePad 以上代码将输出 [a=b, c='123, 456', d=777, e='uij, yyy']

标签: java tokenize


【解决方案1】:

最简单的方法是手动实现一个简单的有限状态机。换句话说,一次处理一个字符的字符串:

  • 当你击中一个空格时,断开一个标记;
  • 当您点击一个引号时,不断获取字符,直到您点击另一个引号。

【讨论】:

  • 有限状态机等同于正则表达式,所以你可以坚持下去,对吧?
  • 请注意,您可能需要处理转义引号,例如 \"
【解决方案2】:

根据原始字符串的格式,您应该能够使用正则表达式作为 java“split”方法的参数:Click here for an example

该示例没有使用您在此任务中需要的正则表达式。

您也可以使用this SO thread 作为指南(尽管它是在 PHP 中),它的功能非常接近您的需要。稍微操纵一下可能会奏效(尽管引号是否是输出的一部分可能会导致一些问题)。请记住,正则表达式在大多数语言中都非常相似。

编辑:在这类任务中走得太远可能会超出正则表达式的能力,因此您可能需要创建一个简单的解析器。

【讨论】:

    【解决方案3】:
    line.split(" (?=[a-z+]=)")
    

    正确给出:

    a=b
    c='123 456'
    d=777
    e='uij yyy'
    

    确保调整 [a-z+] 部分,以防您的键结构发生变化。

    编辑:如果对的值部分中有“=”字符,此解决方案可能会失败。

    【讨论】:

      【解决方案4】:

      StreamTokenizer 可以提供帮助,虽然它最容易设置为在 '=' 上中断,因为它总是会在带引号的字符串的开头中断:

      String s = "Ta=b c='123 456' d=777 e='uij yyy'";
      StreamTokenizer st = new StreamTokenizer(new StringReader(s));
      st.ordinaryChars('0', '9');
      st.wordChars('0', '9');
      while (st.nextToken() != StreamTokenizer.TT_EOF) {
          switch (st.ttype) {
          case StreamTokenizer.TT_NUMBER:
              System.out.println(st.nval);
              break;
          case StreamTokenizer.TT_WORD:
              System.out.println(st.sval);
              break;
          case '=':
              System.out.println("=");
              break;
          default:
              System.out.println(st.sval);
          }
      }
      

      输出

      Ta
      =
      b
      c
      =
      123 456
      d
      =
      777
      e
      =
      uij yyy
      

      如果您省略将数字字符转换为 alpha 的两行,那么您会得到 d=777.0,这可能对您有用。

      【讨论】:

        【解决方案5】:

        假设:

        • 您的变量名(赋值“a=b”中的“a”)的长度可以为 1 或更长
        • 您的变量名(赋值“a=b”中的“a”)不能包含空格字符,其他都可以。
        • 不需要验证您的输入(假定输入采用有效的 a=b 格式)

        这对我来说很好。

        输入:

        a=b abc='123 456' &=777 #='uij yyy' ABC='slk slk'              123sdkljhSDFjflsakd@*#&=456sldSLKD)#(
        

        输出:

        a=b
        abc='123 456'
        &=777
        #='uij yyy'
        ABC='slk slk'             
        123sdkljhSDFjflsakd@*#&=456sldSLKD)#(
        

        代码:

        import java.util.ArrayList;
        import java.util.List;
        import java.util.regex.Matcher;
        import java.util.regex.Pattern;
        
        public class RegexTest {
        
            // SPACE CHARACTER                                          followed by
            // sequence of non-space characters of 1 or more            followed by
            // first occuring EQUALS CHARACTER       
            final static String regex = " [^ ]+?=";
        
        
            // static pattern defined outside so that you don't have to compile it 
            // for each method call
            static final Pattern p = Pattern.compile(regex);
        
            public static List<String> tokenize(String input, Pattern p){
                input = input.trim(); // this is important for "last token case"
                                        // see end of method
                Matcher m = p.matcher(input);
                ArrayList<String> tokens = new ArrayList<String>();
                int beginIndex=0;
                while(m.find()){
                    int endIndex = m.start();
                    tokens.add(input.substring(beginIndex, endIndex));
                    beginIndex = endIndex+1;
                }
        
                // LAST TOKEN CASE
                //add last token
                tokens.add(input.substring(beginIndex));
        
                return tokens;
            }
        
            private static void println(List<String> tokens) {
                for(String token:tokens){
                    System.out.println(token);
                }
            }
        
        
            public static void main(String args[]){
                String test = "a=b " +
                        "abc='123 456' " +
                        "&=777 " +
                        "#='uij yyy' " +
                        "ABC='slk slk'              " +
                        "123sdkljhSDFjflsakd@*#&=456sldSLKD)#(";
                List<String> tokens = RegexTest.tokenize(test, p);
                println(tokens);
            }
        }
        

        【讨论】:

          【解决方案6】:

          或者,使用用于标记化的正则表达式,以及仅将键/值添加到映射的小型状态机:

          String line = "a = b c='123 456' d=777 e =  'uij yyy'";
          Map<String,String> keyval = new HashMap<String,String>();
          String state = "key";
          Matcher m = Pattern.compile("(=|'[^']*?'|[^\\s=]+)").matcher(line);
          String key = null;
          while (m.find()) {
              String found = m.group();
              if (state.equals("key")) {
                  if (found.equals("=") || found.startsWith("'"))
                      { System.err.println ("ERROR"); }
                  else { key = found; state = "equals"; }
              } else if (state.equals("equals")) {
                  if (! found.equals("=")) { System.err.println ("ERROR"); }
                  else { state = "value"; }
              } else if (state.equals("value")) {
                  if (key == null) { System.err.println ("ERROR"); }
                  else {
                      if (found.startsWith("'"))
                          found = found.substring(1,found.length()-1);
                      keyval.put (key, found);
                      key = null;
                      state = "key";
                  }
              }
          }
          if (! state.equals("key"))  { System.err.println ("ERROR"); }
          System.out.println ("map: " + keyval);
          

          打印出来

          map: {d=777, e=uij yyy, c=123 456, a=b}
          

          它会进行一些基本的错误检查,并从值中去掉引号。

          【讨论】:

            【解决方案7】:

            这个解决方案既通用又紧凑(它实际上是 cletus 答案的正则表达式版本):

            String line = "a=b c='123 456' d=777 e='uij yyy'";
            Matcher m = Pattern.compile("('[^']*?'|\\S)+").matcher(line);
            while (m.find()) {
              System.out.println(m.group()); // or whatever you want to do
            }
            

            换句话说,查找所有由引号字符串或非空格字符组合而成的字符;不支持嵌套引号(没有转义字符)。

            【讨论】:

              【解决方案8】:
              public static void main(String[] args) {
              String token;
              String value="";
              HashMap<String, String> attributes = new HashMap<String, String>();
              String line = "a=b c='123  456' d=777 e='uij yyy'";
              StringTokenizer tokenizer = new StringTokenizer(line," ");
              while(tokenizer.hasMoreTokens()){
                      token = tokenizer.nextToken();
                  value = token.contains("'") ? value + " " + token : token ;
                  if(!value.contains("'") || value.endsWith("'")) {
                         //Split the strings and get variables into hashmap 
                         attributes.put(value.split("=")[0].trim(),value.split("=")[1]);
                         value ="";
                  }
              }
                  System.out.println(attributes);
              }
              

              输出: {d=777, a=b, e='uij yyy', c='123 456'}

              在这种情况下,值中的连续空格将被截断为单个空格。 这里属性化的 hashmap 包含值

              【讨论】:

                【解决方案9】:
                 import java.io.*;
                 import java.util.Scanner;
                
                 public class ScanXan {
                  public static void main(String[] args) throws IOException {
                
                    Scanner s = null;
                
                    try {
                        s = new Scanner(new BufferedReader(new FileReader("<file name>")));
                
                        while (s.hasNext()) {
                            System.out.println(s.next());
                           <write for output file>
                        }
                    } finally {
                        if (s != null) {
                            s.close();
                        }
                    }
                 }
                }
                

                【讨论】:

                • 是的@YoungHobbit 我的工作环境 Linux(Ubuntu 15.01) 在 sublime3 上编码。
                【解决方案10】:
                java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(line, " ");
                while (tokenizer.hasMoreTokens()) {
                    String token = tokenizer.nextToken();
                    int index = token.indexOf('=');
                    String key = token.substring(0, index);
                    String value = token.substring(index + 1);
                }
                

                【讨论】:

                  【解决方案11】:

                  您是否尝试过按“=”分割并从每对结果数组中创建一个标记?

                  【讨论】:

                  • 这与问题中提到的 .split() 解决方案有相同的问题。
                  • @rajax 这个解决方案不起作用,但你可以做一些事情,比如拆分一个空格,然后遍历每个拆分字符串:如果它以 ' 开头(假设它的格式很好),那么您只需将这些字符串附加在一起,直到找到以 ' 结尾的字符串。 String Tokenziers 或状态机(或者如果你想通过交替引用类型 ala python 来允许多层嵌套引用,则使用堆栈)可能更有效,但这也可以工作!
                  猜你喜欢
                  • 2013-09-11
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2023-03-11
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多