改变方法,使他们的搜索变得不区分大小写
Alter Methods So That Their Search Becomes Case In-Sensitive
我有两个方法需要修复,以便当用户搜索 "Javascript" 时,如果这些方法拼写为 "Javascript" 或 "JavaScript" - 大小写无关紧要。 LoadData()
已经从 csv 文件(方法执行搜索的地方)导入数据。我正在使用的三个文件(JobData.cs、Program.cs、job_data.csv)的完整代码可以在 here 中找到。
我需要修复的第一个方法是这个(found in JobData.cs, line 143):
public static List<Dictionary<string, string>> FindByValue(string searchTerm)
{
LoadData();
//set up a list of jobs that we're going to use to return from this method
List<Dictionary<string, string>> jobs = new List<Dictionary<string, string>>();
//row is a Dictionary<string, string>
foreach (Dictionary<string, string> row in AllJobs)
{
//item is a KeyValuePair
foreach (KeyValuePair<string, string> field in row)
{
string aValue = field.Value;
if (aValue.Contains(searchTerm))
{
jobs.Add(row);
break;
}
}
}
return jobs;
}
也许一种方法是将 searchTerm
和 value
分开,以便在用户搜索时它们自动变为小写。这样,即使用户键入JAVAScript,它也会自动变成javascript,它会匹配字符串field
中的字符,它也会变成小写。当然我们还是会return字段中的原始字符串,无论是"Javascript"还是"JavaScript"。
另一种方法是自动使 searchTerm
不区分大小写,这样无论大小写如何,它都会与 field.Value
匹配。
这样做会像这样吗?
public static List<Dictionary<string, string>> FindByValue(string searchTerm)
{
LoadData();
//set up a list of jobs that we're going to use to return from this method
List<Dictionary<string, string>> jobs = new List<Dictionary<string, string>>();
//row is a Dictionary<string, string>
foreach (Dictionary<string, string> row in AllJobs)
{
//item is a KeyValuePair
foreach (KeyValuePair<string, string> field in row)
{
string aValue = field.Value;
//create new, case-insensitive searchTerm
culture.CompareInfo.IndexOf(searchTerm, aValue, CompareOptions.IgnoreCase) >= 0
if (aValue.Contains(searchTerm))
{
jobs.Add(row);
break;
}
}
}
return jobs;
}
我正在尝试使用 this example 不区分大小写的字符串比较。但是使用该行会给我错误消息:
The name "culture" does not exist in the current context
The name "CompareOptions" does not exist in the current context
与 field.aValue
相比,关于如何让 searchTerms
不区分大小写的任何其他想法?
让我们首先说明您不能比较字符串并且不区分大小写。它们总是会因为大写字母和小写字母具有不同的 Unicode 值。这是您的解决方案:
如果要全面检查每个字符串,可以使用 ToUpper()
或 ToLower()
方法。例如:
string s1 = "JavaScript";
string s2 = "Javascript";
if (s1.ToLower() == s2.ToLower())
{
//Do something
}
它与 ToUpper()
的工作原理相同,因为:
//s1.ToLower() == "javascript";
//s1.ToUpper() == "JAVASCRIPT";
因此,根据您的情况,假设您的 Dictionary
全部为小写字符串,您可以简单地说:
if (aValue.Contains(searchTerm.ToLower()))
{
//Do something
}
有关这些功能的更多信息,请查看
https://msdn.microsoft.com/en-us/library/e78f86at(v=vs.110).aspx
和
https://msdn.microsoft.com/en-us/library/system.string.toupper(v=vs.110).aspx
多年来我一直在研究这个问题,我想出的最可靠的解决方案是创建一个类似于 String
的包装器,它可以在一个案例中完成所有事情- 不敏感的方式。尽管根据您的需要,这可能有点过头了...
用法是:
using StringOrdinalIgnoreCase = JDanielSmith.System.String<JDanielSmith.System.OrdinalIgnoreCase>;
[TestMethod]
public void StringOrdinalIgnoreCaseDictionary()
{
var d = new Dictionary<StringOrdinalIgnoreCase, int>() { { "abc", 1 }, { "def", 2 } };
Assert.IsTrue(d.ContainsKey("ABC"));
try
{
d.Add("DEF", 2);
Assert.Fail();
}
catch (ArgumentException) { }
}
String.cs
using System;
using CodeAnalysis = System.Diagnostics.CodeAnalysis;
namespace MyNS.System
{
/// <summary>
/// Provide a case-insensitive wrapper around System.String.
///
/// This is especially useful when using strings as keys in collections, where the key is something like a Windows file-system pathname;
/// it can be easy to forget to pass an IEqualityComparer<> in the constructor.
///
/// Some hints from:
/// </summary>
[CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "String")]
[CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1036:OverrideMethodsOnComparableTypes")]
public sealed class String<TComparerAndComparison> : IComparable, ICloneable,
IComparable<String<TComparerAndComparison>>, IEquatable<String<TComparerAndComparison>>,
IComparable<String>, IEquatable<String>
where TComparerAndComparison : StringComparerAndComparison, new()
{
static readonly StringComparerAndComparison _comparerAndComparison = new TComparerAndComparison();
static readonly StringComparer _comparer = _comparerAndComparison.Comparer;
static readonly StringComparison _comparisonType = _comparerAndComparison.Comparison;
public string Value { get; }
public String(string value)
{
// matching the behavior of System.String is more straight-forward if "Value" is never null
Value = value ?? String.Empty;
}
// easily convert to/from System.String
public static implicit operator String<TComparerAndComparison>(string source) => new String<TComparerAndComparison>(source);
public static implicit operator string(String<TComparerAndComparison> source) => source?.Value;
#region Equals, IEquatable
public override bool Equals(object obj)
{
if (Object.ReferenceEquals(obj, null))
return false; // this != null
var other = obj as String<TComparerAndComparison>;
if (!Object.ReferenceEquals(other, null))
return Equals(other); // call Equals(String<TStringComparerAndComparison>)
var s_other = obj as string;
if (!Object.ReferenceEquals(s_other, null))
return Equals(s_other); // call Equals(string)
return _comparer.Equals(obj);
}
public bool Equals(String<TComparerAndComparison> other)
{
if (Object.ReferenceEquals(other, null))
return false; // this != null
return Equals(other.Value); // call Equals(string)
}
public bool Equals(string other) => _comparer.Equals(Value, other);
public override int GetHashCode()
{
return _comparer.GetHashCode(Value);
}
#endregion
public override string ToString() => Value;
public object Clone() => new String<TComparerAndComparison>(Value);
#region IComparable
public int CompareTo(object obj)
{
// https://msdn.microsoft.com/en-us/library/4d7sx9hd(v=vs.110).aspx
if (Object.ReferenceEquals(obj, null))
return 1; // If other is not a valid object reference, this instance is greater.
// obj must be either StringOrdinalIgnoreCase or String
var other = obj as String<TComparerAndComparison>;
if (Object.ReferenceEquals(other, null))
{
var s_other = obj as string;
if (Object.ReferenceEquals(s_other, null))
throw new ArgumentException("Object must be of type " + nameof(String<TComparerAndComparison>) + " or String.");
return CompareTo(s_other); // call CompareTo(string)
}
return CompareTo(other); // call CompareTo(StringOrdinalIgnoreCase)
}
public int CompareTo(String<TComparerAndComparison> other)
{
// https://msdn.microsoft.com/en-us/library/4d7sx9hd(v=vs.110).aspx
if (Object.ReferenceEquals(other, null))
return 1; // If other is not a valid object reference, this instance is greater.
if (Object.ReferenceEquals(Value, other.Value))
return 0;
return CompareTo(other.Value); // call CompareTo(string)
}
public int CompareTo(string other)
{
// https://msdn.microsoft.com/en-us/library/4d7sx9hd(v=vs.110).aspx
if (Object.ReferenceEquals(other, null))
return 1; // If other is not a valid object reference, this instance is greater.
return _comparer.Compare(Value, other);
}
public static bool operator ==(String<TComparerAndComparison> x, String<TComparerAndComparison> y)
{
if (Object.ReferenceEquals(x, null))
return Object.ReferenceEquals(y, null); // null == null, null != something
return x.Equals(y); // know x != null
}
public static bool operator ==(String<TComparerAndComparison> x, string y)
{
if (Object.ReferenceEquals(x, null))
return Object.ReferenceEquals(y, null); // null == null, null != something
return x.Equals(y); // know x != null
}
public static bool operator ==(string x, String<TComparerAndComparison> y) => y == x; // == is commutative, x == y
public static bool operator !=(String<TComparerAndComparison> x, String<TComparerAndComparison> y) => !(x == y);
public static bool operator !=(string x, String<TComparerAndComparison> y) => !(x == y);
public static bool operator !=(String<TComparerAndComparison> x, string y) => !(x == y);
#endregion
#region IndexOf, LastIndexOf, StartsWith, EndsWith
public bool EndsWith(string value) => Value.EndsWith(value, _comparisonType);
public int IndexOf(string value) => Value.IndexOf(value, _comparisonType);
public int IndexOf(string value, int startIndex) => Value.IndexOf(value, startIndex, _comparisonType);
public int IndexOf(string value, int startIndex, int count) => Value.IndexOf(value, startIndex, count, _comparisonType);
public int LastIndexOf(string value) => Value.LastIndexOf(value, _comparisonType);
public int LastIndexOf(string value, int startIndex) => Value.LastIndexOf(value, startIndex, _comparisonType);
public int LastIndexOf(string value, int startIndex, int count) => Value.LastIndexOf(value, startIndex, count, _comparisonType);
public bool StartsWith(string value) => Value.StartsWith(value, _comparisonType);
#endregion
}
}
StringComparerAndComparison.cs
using System;
using StringComparer = System.StringComparer;
using StringComparison = System.StringComparison;
namespace JDanielSmith.System
{
/// <summary>
/// Pass around System.StringComparer and System.StringComparison together.
/// Also, provides a base class for generics.
/// </summary>
public abstract class StringComparerAndComparison
{
internal StringComparer Comparer { get; }
internal StringComparison Comparison { get; }
internal StringComparerAndComparison(StringComparer comparer, StringComparison comparison)
{
if (comparer == null) throw new ArgumentNullException(nameof(comparer));
Comparer = comparer;
Comparison = comparison;
}
}
public sealed class CurrentCulture : StringComparerAndComparison
{
public CurrentCulture() : base(StringComparer.CurrentCulture, StringComparison.CurrentCulture) { }
}
public sealed class CurrentCultureIgnoreCase : StringComparerAndComparison
{
public CurrentCultureIgnoreCase() : base(StringComparer.CurrentCultureIgnoreCase, StringComparison.CurrentCultureIgnoreCase) { }
}
public sealed class InvariantCulture : StringComparerAndComparison
{
public InvariantCulture() : base(StringComparer.InvariantCulture, StringComparison.InvariantCulture) { }
}
public sealed class InvariantCultureIgnoreCase : StringComparerAndComparison
{
public InvariantCultureIgnoreCase() : base(StringComparer.InvariantCultureIgnoreCase, StringComparison.InvariantCultureIgnoreCase) { }
}
public sealed class Ordinal : StringComparerAndComparison
{
public Ordinal() : base(StringComparer.Ordinal, StringComparison.Ordinal) { }
}
public sealed class OrdinalIgnoreCase : StringComparerAndComparison
{
public OrdinalIgnoreCase() : base(StringComparer.OrdinalIgnoreCase, StringComparison.OrdinalIgnoreCase) { }
}
}
您使用 IndexOf
而不是 Contains
的想法是正确的,您只需使用正确的重载 (the one that takes a StringComparison
option)。不区分大小写的比较有多个选项。我正在使用 OrdinalIgnoreCase
,但您可以使用最适合您的那个。
而不是这个:
aValue.Contains(searchTerm)
这样写:
aValue.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase) >= 0
我有两个方法需要修复,以便当用户搜索 "Javascript" 时,如果这些方法拼写为 "Javascript" 或 "JavaScript" - 大小写无关紧要。 LoadData()
已经从 csv 文件(方法执行搜索的地方)导入数据。我正在使用的三个文件(JobData.cs、Program.cs、job_data.csv)的完整代码可以在 here 中找到。
我需要修复的第一个方法是这个(found in JobData.cs, line 143):
public static List<Dictionary<string, string>> FindByValue(string searchTerm)
{
LoadData();
//set up a list of jobs that we're going to use to return from this method
List<Dictionary<string, string>> jobs = new List<Dictionary<string, string>>();
//row is a Dictionary<string, string>
foreach (Dictionary<string, string> row in AllJobs)
{
//item is a KeyValuePair
foreach (KeyValuePair<string, string> field in row)
{
string aValue = field.Value;
if (aValue.Contains(searchTerm))
{
jobs.Add(row);
break;
}
}
}
return jobs;
}
也许一种方法是将 searchTerm
和 value
分开,以便在用户搜索时它们自动变为小写。这样,即使用户键入JAVAScript,它也会自动变成javascript,它会匹配字符串field
中的字符,它也会变成小写。当然我们还是会return字段中的原始字符串,无论是"Javascript"还是"JavaScript"。
另一种方法是自动使 searchTerm
不区分大小写,这样无论大小写如何,它都会与 field.Value
匹配。
这样做会像这样吗?
public static List<Dictionary<string, string>> FindByValue(string searchTerm)
{
LoadData();
//set up a list of jobs that we're going to use to return from this method
List<Dictionary<string, string>> jobs = new List<Dictionary<string, string>>();
//row is a Dictionary<string, string>
foreach (Dictionary<string, string> row in AllJobs)
{
//item is a KeyValuePair
foreach (KeyValuePair<string, string> field in row)
{
string aValue = field.Value;
//create new, case-insensitive searchTerm
culture.CompareInfo.IndexOf(searchTerm, aValue, CompareOptions.IgnoreCase) >= 0
if (aValue.Contains(searchTerm))
{
jobs.Add(row);
break;
}
}
}
return jobs;
}
我正在尝试使用 this example 不区分大小写的字符串比较。但是使用该行会给我错误消息:
The name "culture" does not exist in the current context
The name "CompareOptions" does not exist in the current context
与 field.aValue
相比,关于如何让 searchTerms
不区分大小写的任何其他想法?
让我们首先说明您不能比较字符串并且不区分大小写。它们总是会因为大写字母和小写字母具有不同的 Unicode 值。这是您的解决方案:
如果要全面检查每个字符串,可以使用 ToUpper()
或 ToLower()
方法。例如:
string s1 = "JavaScript";
string s2 = "Javascript";
if (s1.ToLower() == s2.ToLower())
{
//Do something
}
它与 ToUpper()
的工作原理相同,因为:
//s1.ToLower() == "javascript";
//s1.ToUpper() == "JAVASCRIPT";
因此,根据您的情况,假设您的 Dictionary
全部为小写字符串,您可以简单地说:
if (aValue.Contains(searchTerm.ToLower()))
{
//Do something
}
有关这些功能的更多信息,请查看
https://msdn.microsoft.com/en-us/library/e78f86at(v=vs.110).aspx
和
https://msdn.microsoft.com/en-us/library/system.string.toupper(v=vs.110).aspx
多年来我一直在研究这个问题,我想出的最可靠的解决方案是创建一个类似于 String
的包装器,它可以在一个案例中完成所有事情- 不敏感的方式。尽管根据您的需要,这可能有点过头了...
用法是:
using StringOrdinalIgnoreCase = JDanielSmith.System.String<JDanielSmith.System.OrdinalIgnoreCase>;
[TestMethod]
public void StringOrdinalIgnoreCaseDictionary()
{
var d = new Dictionary<StringOrdinalIgnoreCase, int>() { { "abc", 1 }, { "def", 2 } };
Assert.IsTrue(d.ContainsKey("ABC"));
try
{
d.Add("DEF", 2);
Assert.Fail();
}
catch (ArgumentException) { }
}
String.cs
using System;
using CodeAnalysis = System.Diagnostics.CodeAnalysis;
namespace MyNS.System
{
/// <summary>
/// Provide a case-insensitive wrapper around System.String.
///
/// This is especially useful when using strings as keys in collections, where the key is something like a Windows file-system pathname;
/// it can be easy to forget to pass an IEqualityComparer<> in the constructor.
///
/// Some hints from:
/// </summary>
[CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "String")]
[CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1036:OverrideMethodsOnComparableTypes")]
public sealed class String<TComparerAndComparison> : IComparable, ICloneable,
IComparable<String<TComparerAndComparison>>, IEquatable<String<TComparerAndComparison>>,
IComparable<String>, IEquatable<String>
where TComparerAndComparison : StringComparerAndComparison, new()
{
static readonly StringComparerAndComparison _comparerAndComparison = new TComparerAndComparison();
static readonly StringComparer _comparer = _comparerAndComparison.Comparer;
static readonly StringComparison _comparisonType = _comparerAndComparison.Comparison;
public string Value { get; }
public String(string value)
{
// matching the behavior of System.String is more straight-forward if "Value" is never null
Value = value ?? String.Empty;
}
// easily convert to/from System.String
public static implicit operator String<TComparerAndComparison>(string source) => new String<TComparerAndComparison>(source);
public static implicit operator string(String<TComparerAndComparison> source) => source?.Value;
#region Equals, IEquatable
public override bool Equals(object obj)
{
if (Object.ReferenceEquals(obj, null))
return false; // this != null
var other = obj as String<TComparerAndComparison>;
if (!Object.ReferenceEquals(other, null))
return Equals(other); // call Equals(String<TStringComparerAndComparison>)
var s_other = obj as string;
if (!Object.ReferenceEquals(s_other, null))
return Equals(s_other); // call Equals(string)
return _comparer.Equals(obj);
}
public bool Equals(String<TComparerAndComparison> other)
{
if (Object.ReferenceEquals(other, null))
return false; // this != null
return Equals(other.Value); // call Equals(string)
}
public bool Equals(string other) => _comparer.Equals(Value, other);
public override int GetHashCode()
{
return _comparer.GetHashCode(Value);
}
#endregion
public override string ToString() => Value;
public object Clone() => new String<TComparerAndComparison>(Value);
#region IComparable
public int CompareTo(object obj)
{
// https://msdn.microsoft.com/en-us/library/4d7sx9hd(v=vs.110).aspx
if (Object.ReferenceEquals(obj, null))
return 1; // If other is not a valid object reference, this instance is greater.
// obj must be either StringOrdinalIgnoreCase or String
var other = obj as String<TComparerAndComparison>;
if (Object.ReferenceEquals(other, null))
{
var s_other = obj as string;
if (Object.ReferenceEquals(s_other, null))
throw new ArgumentException("Object must be of type " + nameof(String<TComparerAndComparison>) + " or String.");
return CompareTo(s_other); // call CompareTo(string)
}
return CompareTo(other); // call CompareTo(StringOrdinalIgnoreCase)
}
public int CompareTo(String<TComparerAndComparison> other)
{
// https://msdn.microsoft.com/en-us/library/4d7sx9hd(v=vs.110).aspx
if (Object.ReferenceEquals(other, null))
return 1; // If other is not a valid object reference, this instance is greater.
if (Object.ReferenceEquals(Value, other.Value))
return 0;
return CompareTo(other.Value); // call CompareTo(string)
}
public int CompareTo(string other)
{
// https://msdn.microsoft.com/en-us/library/4d7sx9hd(v=vs.110).aspx
if (Object.ReferenceEquals(other, null))
return 1; // If other is not a valid object reference, this instance is greater.
return _comparer.Compare(Value, other);
}
public static bool operator ==(String<TComparerAndComparison> x, String<TComparerAndComparison> y)
{
if (Object.ReferenceEquals(x, null))
return Object.ReferenceEquals(y, null); // null == null, null != something
return x.Equals(y); // know x != null
}
public static bool operator ==(String<TComparerAndComparison> x, string y)
{
if (Object.ReferenceEquals(x, null))
return Object.ReferenceEquals(y, null); // null == null, null != something
return x.Equals(y); // know x != null
}
public static bool operator ==(string x, String<TComparerAndComparison> y) => y == x; // == is commutative, x == y
public static bool operator !=(String<TComparerAndComparison> x, String<TComparerAndComparison> y) => !(x == y);
public static bool operator !=(string x, String<TComparerAndComparison> y) => !(x == y);
public static bool operator !=(String<TComparerAndComparison> x, string y) => !(x == y);
#endregion
#region IndexOf, LastIndexOf, StartsWith, EndsWith
public bool EndsWith(string value) => Value.EndsWith(value, _comparisonType);
public int IndexOf(string value) => Value.IndexOf(value, _comparisonType);
public int IndexOf(string value, int startIndex) => Value.IndexOf(value, startIndex, _comparisonType);
public int IndexOf(string value, int startIndex, int count) => Value.IndexOf(value, startIndex, count, _comparisonType);
public int LastIndexOf(string value) => Value.LastIndexOf(value, _comparisonType);
public int LastIndexOf(string value, int startIndex) => Value.LastIndexOf(value, startIndex, _comparisonType);
public int LastIndexOf(string value, int startIndex, int count) => Value.LastIndexOf(value, startIndex, count, _comparisonType);
public bool StartsWith(string value) => Value.StartsWith(value, _comparisonType);
#endregion
}
}
StringComparerAndComparison.cs
using System;
using StringComparer = System.StringComparer;
using StringComparison = System.StringComparison;
namespace JDanielSmith.System
{
/// <summary>
/// Pass around System.StringComparer and System.StringComparison together.
/// Also, provides a base class for generics.
/// </summary>
public abstract class StringComparerAndComparison
{
internal StringComparer Comparer { get; }
internal StringComparison Comparison { get; }
internal StringComparerAndComparison(StringComparer comparer, StringComparison comparison)
{
if (comparer == null) throw new ArgumentNullException(nameof(comparer));
Comparer = comparer;
Comparison = comparison;
}
}
public sealed class CurrentCulture : StringComparerAndComparison
{
public CurrentCulture() : base(StringComparer.CurrentCulture, StringComparison.CurrentCulture) { }
}
public sealed class CurrentCultureIgnoreCase : StringComparerAndComparison
{
public CurrentCultureIgnoreCase() : base(StringComparer.CurrentCultureIgnoreCase, StringComparison.CurrentCultureIgnoreCase) { }
}
public sealed class InvariantCulture : StringComparerAndComparison
{
public InvariantCulture() : base(StringComparer.InvariantCulture, StringComparison.InvariantCulture) { }
}
public sealed class InvariantCultureIgnoreCase : StringComparerAndComparison
{
public InvariantCultureIgnoreCase() : base(StringComparer.InvariantCultureIgnoreCase, StringComparison.InvariantCultureIgnoreCase) { }
}
public sealed class Ordinal : StringComparerAndComparison
{
public Ordinal() : base(StringComparer.Ordinal, StringComparison.Ordinal) { }
}
public sealed class OrdinalIgnoreCase : StringComparerAndComparison
{
public OrdinalIgnoreCase() : base(StringComparer.OrdinalIgnoreCase, StringComparison.OrdinalIgnoreCase) { }
}
}
您使用 IndexOf
而不是 Contains
的想法是正确的,您只需使用正确的重载 (the one that takes a StringComparison
option)。不区分大小写的比较有多个选项。我正在使用 OrdinalIgnoreCase
,但您可以使用最适合您的那个。
而不是这个:
aValue.Contains(searchTerm)
这样写:
aValue.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase) >= 0