Converting C# method to quote SQL identifiers to F#
I'm porting the following method (QuoteIdentifier) from C# to F#. It quotes SQL identifiers (also handling embedded and incorrect quotes).
For example: QuoteIdentifier("dbo.mytable", "[", "]") outputs "[dbo].[mytable]"
Unfor开发者_运维问答tunately, the F# code is even less readable than the C#. Is there a better way to write this in F#?
C# original:
public static string QuoteIdentifier(string id, string quotePrefix, string quoteSuffix) {
if (String.IsNullOrEmpty(id))
return id;
return String.Join(".", SplitIdentifier(id, quotePrefix, quoteSuffix));
}
private static string[] SplitIdentifier(string id, string quotePrefix, string quoteSuffix) {
if (String.IsNullOrEmpty(id))
return new string[] { id };
List<string> list = new List<string>();
int index = 0;
do {
list.Add(GetNextIdentifier(id, quotePrefix, quoteSuffix, ref index));
} while (index != id.Length);
return list.ToArray();
}
private static string GetNextIdentifier(string id, string quotePrefix, string quoteSuffix, ref int startIndex) {
int index = startIndex;
bool quoted = false;
StringBuilder builder = new StringBuilder();
if (!String.IsNullOrEmpty(quotePrefix)) {
builder.Append(quotePrefix);
quoted = (String.CompareOrdinal(id, startIndex, quotePrefix, 0, quotePrefix.Length) == 0);
if (quoted)
index += quotePrefix.Length;
}
for (int i = index; i < id.Length; i++) {
if (!String.IsNullOrEmpty(quoteSuffix) && String.CompareOrdinal(id, i, quoteSuffix, 0, quoteSuffix.Length) == 0) {
if ((i + quoteSuffix.Length) == id.Length) {
index = id.Length;
break;
}
if (id[i + quoteSuffix.Length] == '.') {
index = (i + quoteSuffix.Length + 1);
break;
}
builder.Append(quoteSuffix).Append(quoteSuffix);
if (String.CompareOrdinal(id, (i + quoteSuffix.Length), quoteSuffix, 0, quoteSuffix.Length) == 0)
i++;
} else {
index = (i + 1);
if (!quoted && id[i] == '.')
break;
builder.Append(id[i]);
}
}
if (!String.IsNullOrEmpty(quoteSuffix))
builder.Append(quoteSuffix);
startIndex = index;
return builder.ToString();
}
F# rewrite:
[<CompiledName("QuoteIdentifier")>]
let quoteIdentifier id quotePrefix quoteSuffix =
let isEmpty = String.IsNullOrEmpty
let notEmpty = not << isEmpty
let prefix, suffix = quotePrefix, quoteSuffix
let equal strA indexA strB = (String.CompareOrdinal(strA, indexA, strB, 0, strB.Length) = 0)
let getNext start =
let builder = new StringBuilder()
let append (s:string) = builder.Append(s) |> ignore
let quoted =
if notEmpty prefix then
append prefix
equal id start prefix
else false
let index = if quoted then start + prefix.Length else start
let rec loop i n =
if i = id.Length then (i, n)
else
if notEmpty suffix && equal id i suffix then
if (i + suffix.Length) = id.Length then (i, id.Length)
elif id.[i + suffix.Length] = '.' then (i, i + suffix.Length + 1)
else
append suffix
append suffix
loop (if (equal id (i + suffix.Length) suffix) then i + 2 else i + 1) n
else
if not quoted && id.[i] = '.' then (i, i + 1)
else
append (id.[i].ToString())
loop (i + 1) (i + 1)
let _, next = loop index index
if notEmpty suffix then append suffix
(builder.ToString(), next)
let split() =
0
|> Seq.unfold (function
| i when i = id.Length -> None
| i -> Some (getNext i))
|> Seq.toArray
if isEmpty id then id
else String.Join(".", split())
May I suggest a simpler version of the C# code? I can't test it right now, but something like this should work:
public static string QuoteIdentifier(string id, string quotePrefix, string quoteSuffix) {
if (String.IsNullOrEmpty(id)) {
return id;
}
var identifiers = id.Split(new char[] {'.'}, StringSplitOptions.RemoveEmptyEntries);
var separator = string.Format("{0}.{1}", quoteSuffix, quotePrefix);
var quotedString = string.Join(separator, identifiers);
return string.Format("{0}{1}{2}", quotePrefix, quotedString, quoteSuffix);
}
Once you have this working, you can convert it to a also short F# version.
精彩评论