【问题标题】:equal() and equalsIgnoreCase() return false for equal stringsequal() 和 equalsIgnoreCase() 对相等的字符串返回 false
【发布时间】:2019-06-13 03:00:49
【问题描述】:

我在 Mac 上使用 Eclipse IDE(版本:3.4.2),遇到了以下问题。

当使用 equal() 或 equalsIgnoreCase() 方法比较字符串时,即使字符串相等,我也会收到 false。例如,下面的代码将以下条件视为 false,即使 values[0] = "debug_mode"

if (values[0].equalsIgnoreCase("debug_mode")) 
    debug_mode = true;

这是以下循环的一部分:

String value = dis.readLine();
String values[] = value.trim().split("=");
if (values.length >= 2)
{
    Config.prnt_dbg_msg(values[0] + "\t" + values[1]);
    if (values[0].equalsIgnoreCase("debug_mode")) 
        debug_mode = isTrue(values[1]);
    if (values[0].equalsIgnoreCase("debug_query_parsing")) 
        debug_query_parsing = isTrue(values[1]);
    if (values[0].equalsIgnoreCase("username")) 
        Connection_Manager.alterAccessParameters(values[1], null, null);
    if (values[0].equalsIgnoreCase("password")) 
        Connection_Manager.alterAccessParameters(null, values[1], null);
if (values[0].equalsIgnoreCase("database")) 
        Connection_Manager.alterAccessParameters(null, null, values[1]);
    if (values[0].equalsIgnoreCase("allow_duplicate_entries")) 
        allow_duplicate_entries = isTrue(values[1]);
}                         

我尝试使用value[0].equal("debug_mode") 并得到相同的结果。 有人知道为什么吗?

【问题讨论】:

  • values[0]的实际值是多少
  • 你 110% 确定 values[0] 包含一个值为“debug_mode”的字符串吗?将其打印到控制台以确保。
  • if 条件之前你能打印 values[0] 吗?
  • @Evan - 甚至 120%,这是我能做到的。我在调试代码期间和标准输出中观察到了这个参数。
  • 如果你使用的是调试器,你能进入 equalsIgnoreCase() 看看它在里面做什么吗?

标签: java string string-comparison


【解决方案1】:

那确实很奇怪:) 你能把上面的代码改成这样吗:

if ("debug_mode".equalsIgnoreCase("debug_mode")) 
    debug_mode = true;

确认它工作正常,然后仔细检查为什么您的 values[0] 不是“debug_mode”。

以下是我现在想到的要检查的事项列表:

  • 检查values[0].length() == "debug_mode".length()
  • 我非常怀疑,但还是让我把它放在桌面上 - 你有没有机会使用 Unicode?​​li>
  • 您能否打印每个字符并在该字符和“debug_mode”字符串的相应字符之间执行.equals()
  • 如果这是在一个更大的项目中,您能否在一个简单的 Java 项目中做同样的事情并确认它在那里工作?

为了澄清,问题实际上是使用DataInputStream.readLine。来自 javadoc (http://download.oracle.com/javase/1.6.0/docs/api/java/io/DataInputStream.html):

readLine()
      Deprecated. This method does not properly convert bytes to characters. ...

它实际上以一种微妙的方式与 Unicode 有关 - 当您执行 writeChar 时,您实际上写入了两个字节 097,字母 a 的大端 Unicode。

这是一个显示行为的独立 sn-p:

import java.io.*;
import java.util.*;

public class B {
  public static void main(String[] args) throws Exception {
    String os = "abc";

    System.out.println("---- unicode, big-endian");
    for(byte b: os.getBytes("UTF-16BE")) {
      System.out.println(b);
    }

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    DataOutputStream dos = new DataOutputStream(baos);

    for(char c: os.toCharArray()) {
      dos.writeChar(c);
    }

    byte[] ba = baos.toByteArray();

    System.out.println("---- ba");
    for(byte b: ba) {
      System.out.println(b);
    }

    ByteArrayInputStream bais = new ByteArrayInputStream(ba);
    DataInputStream dis = new DataInputStream(bais);

    System.out.println("---- dis");
    String s = dis.readLine();
    System.out.println(s);
    System.out.println("String length is " + s.length() 
      + ", but you would expect " + os.length() 
      + ", as that is what you see printed...");
  }
}

故事的寓意 - 不要使用已弃用的 API...此外,空格是无声杀手:http://www.codinghorror.com/blog/2009/11/whitespace-the-silent-killer.html

【讨论】:

  • 好吧,您对此表示高度怀疑,但实际上问题是我使用 DataOutputStream.writeChar() 而不是 writeUTF() 写入文件。它确实导致了问题。
  • 与它无关。如果 writeUTF() 有效而 writeChar() 无效,那么他一定是在使用 readUTF()读取,这肯定不适用于不是 writeUTF() 写入的数据。 Unicode 与此无关。
  • @EJP 它是 Unicode 和已弃用 API 的混合体(后者更重要) - 请参阅编辑。
  • 所以它与 writeUTF() 无关,也与弃用无关。它与使用非对称方法的读写有关。如果您使用 writeChars() 编写,则需要使用 readChar() 读取。如果您使用 writeUTF() 编写,则需要使用 readUTF() 读取。如果您使用 PrintStream.println() 编写,则需要使用 readLine() 阅读(弃用与否)。
  • 并非如此 - 如果您通过调用 writeChar 5 次写入,最后一个写入换行符,您会期望通过调用一次 readLine 将所有 5 个字符读入字符串.我想说这是所有这些因素和readLine(相对于writeUTF)的模糊性的组合。这就是 readLine 被弃用的原因。明确同意你应该尽可能地保持对称。
【解决方案2】:

我刚刚遇到了同样的问题,使用 equalsIgnoreCase。

盯着屏幕看了几个小时,调试代码后,我突然意识到我的 if 语句有一个 ;最后,

if ("stupid".equalsIgnoreCase.("STupid");
{
     //it always gets here 

}

希望这对将来的人有所帮助。

【讨论】:

  • 因为那个愚蠢的错误,我以为你是个愚蠢的人,但是……我心想:“这总是人为错误,所以,让我检查一下我的代码”。发生了什么?这是你同样的错误! :P 我有将近一个小时的时间
  • 同样,我在尝试从使用此类检查的方法返回时遇到问题。 if ("stupid".equalsIgnoreCase.("STupid")) return;我不得不包装返回块:if ("stupid".equalsIgnoreCase.("STupid")) { return; }
  • 您也可以通过将开头的 { 与 IF 语句放在同一行来避免这个潜在的问题。我就是做这个的。这样,如果您添加了一个意外的“;”由于括号不匹配,您几乎肯定会在任何地方导致编译问题。
【解决方案3】:

我和其他人在一起,这太疯狂了,不应该发生。我同意将其打印出来可能会有所帮助,但我假设您已经尝试过了。

这可能是本地化问题吗?也就是说,当您在编辑器中输入 debug_mode 时(对于字符串),它是字符串“debug_mode”,但是当您在执行期间输入字符串时,终端设置为使用不同的语言并且您得到不同的(但是外观相同)字符?

要找出答案,请遍历输入的字符串并打印出每个字符的整数值,然后对硬编码的字符串执行相同操作,看看它们是否相同。

String value = dis.readLine();
String values[] = value.trim().split("=");

System.out.println("Input:");

for (int i = 0; i < values[0].length(); i++) {
    System.out.print((int) values[0].charAt(i));
    System.out.print(' ');
}

System.out.println("Hardcoded:");

String debugMode = "debug_mode";

for (int i = 0; i < debugMode.length(); i++) {
    System.out.print((int) debugMode.charAt(i));
    System.out.print(' ');
}

现在要使其工作,您必须键入代码(或至少 debug_mode 常量),使其具有与您使用的相同的字符集。

我愿意赌一大笔钱,这不是问题,但即使不是,它也应该证明是有启发性的,并告诉你有什么不同。

【讨论】:

    【解决方案4】:

    试试compareToIgnoreCase:

    if (values[0].compareToIgnoreCase("debug_mode") != 0) 
        debug_mode = true;
    

    如果 那个 不起作用,请改用compareTo

    如果那个不起作用,试试:

    String d = (String)values[0];
    if (d.compareToIgnoreCase("debug_mode") != 0) 
            debug_mode = true;
    

    如果 那些 不工作,你有一个 严重的 Java 问题。要么它很古老,要么它不喜欢你。

    【讨论】:

    • 没有这些方法不能正常工作的古代或现代 Java 版本。一开始它就无法自行编译。
    【解决方案5】:

    虽然上面很少有非常好的和正确的答案,但我仍然想提一下我的个人经历,以便任何遇到同样问题的人都可以从这个答案中获得即时帮助。

    我有两个不同的字符串,说 string Astring B 来自不同的来源,它们看起来和我一模一样但是 我使用equals方法对他们来说变得不相等

    即使使用 equalsIgnoreCase 给了我 false

    我一无所知,因为当我打印那些字符串 (A & B) 以检查它们的样子时

    String A is dsycuii343qzx899+ty=
    String B is dsycuii343qzx899+ty=
    

    所以,然后我检查了两个字符串的长度,这给了我线索

    String A length = 20
    String B length = 21
    

    所以这意味着我可能会遗漏一些东西,

    所以我做的是

    我按字符检查了每个字符串字符,我知道了问题

    看起来像 dsycuii343qzx899+ty= 的字符串 A 实际上是 dsycuii343qzx899+ty=\n

    即在日志检查时注意到末尾有一个LF(换行符)

    希望它可以帮助某人。

    【讨论】:

      【解决方案6】:

      您可以在 Android 上使用 SpannableString 轻松遇到此问题,就像 TextView 启用自动链接时,例如:

      // Outputs "a string"
      Log.d("test", "TextView text: " + textView.getText());
      
      // Outputs "a string"
      Log.d("test", "Text to match: " + "a string");
      
      if( textView.getText().equals("a string") )
      {
          // Won't get here
      }
      

      你可以做一个快速测试,看看 textView.getText() 会返回什么样的字符串:

      Log.d("test", "String class: " + textView.getText().getClass().getSimpleName());
      

      如果您确实有一个SpannableString,您只需对其调用 toString() 即可满足 if 条件:

      if( textView.getText().toString().equals("a string") )
      {
          // We're here
      }
      

      【讨论】:

        【解决方案7】:

        另一方面,我在比较检索到的表的“状态”时遇到了类似问题的 JSP 页面:

        try{
        
        
        
          // ID is a Primary Key (unique). STATUS column data type in DB: CHAR(20)
          rs = stmt.executeQuery("select STATUS from TEMP_TABLE WHERE ID='"+id+"'");
        
          while(rs.next()){
        
                status = (String) rs.getString("STATUS");
        
          }
           if ( status.equalsIgnoreCase("active") )
           {
                  // Run some DB Queries
           } else {
                   out.write("Page can't be accessed due to status : " + status);
           }
        } catch(Exception e) { e.getMessage(); }
        finally {
                 //close all open objects
        }
        

        由于我不知道的原因,它总是在 else 块中显示消息“由于状态:活动而无法访问页面”,尽管状态为“活动”。在运行此查询之前和之后,我尝试在每个查询之后关闭 rs 和 stmt 对象,但这没有帮助。最终我将查询更改为

        "select STATUS from TEMP_TABLE WHERE ID='"+id+"' where STATUS='ACTIVE'"
        

        【讨论】:

          【解决方案8】:

          我认为问题可能在于,虽然实际的 String 值相等,但它们底层的 byte[] 可能不相等。

          试试用这个方法比较两个byte[]的:

          private String printBytes(String str) {
              byte[] bytes = str.getBytes(ENCODING);
              String output = "byte[";
              for (int i = 0; i < bytes.length; i++) {
                  output += Byte.toString(bytes[i]);
                  if (i < bytes.length - 1) {
                      output += ", ";
                  }
              }
              output += "]";
              return output;
          }
          

          例如:

          Charset ENCODING = Charset.forName("UTF-8");
          Log.d(LOG_TAG, "string1: \"" + string1 + "\" - " + printBytes(string1));
          Log.d(LOG_TAG, "string2: \"" + string2 + "\" - " + printBytes(string2));
          

          这样可以进行视觉比较。对于长 Strings,您可以通过同时遍历两个数组并比较值来以编程方式比较 byte[]

          【讨论】:

            【解决方案9】:

            就我而言,我刚刚发现一个字符串在字符串之前有空格。 我的字符串就像“SUCCESS”和“SUCCESS”, 所以它返回false。我用过:

             String st1=st.replaceAll("\\s","");
            

            这样问题就解决了。

            【讨论】:

              【解决方案10】:

              我的回答可能很晚,但对某人有用。

              在比较之前只需修剪()两个字符串

                     eg: if(data1.trim().equalsIgnoreCase(data2.trim()))
                          {
                              //This gives proper result
                          }
              

              【讨论】:

                【解决方案11】:

                可以是字节序标记:https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8

                试试 str1.length() 和 str2.length() 如果不一样的话 str1.charAt(0) 和 y=if 你得到像 '\uFEFF' 65279 这样的东西是你的问题

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-07-31
                  • 2016-12-18
                  相关资源
                  最近更新 更多