c# - txt - Eliminar todas las líneas de espacios en blanco de una cadena de múltiples líneas de manera eficiente
salto de linea en asp net c# (18)
Aquí hay algo simple si se trabaja contra cada línea individual ...
(^/s+|/s+|^)$
En C #, ¿cuál es la mejor manera de eliminar líneas en blanco, es decir, líneas que contienen solo espacios en blanco de una cadena? Me complace utilizar un Regex si esa es la mejor solución.
EDITAR: Debería agregar que estoy usando .NET 2.0.
Actualización de la recompensa : revertiré esto después de que se otorgue la recompensa, pero quería aclarar algunas cosas.
Primero, cualquier expresión regular de Perl 5 funcionará. Esto no se limita a los desarrolladores de .NET. El título y las etiquetas se han editado para reflejar esto.
Segundo, aunque di un ejemplo rápido en los detalles de la recompensa, no es la única prueba que debes cumplir. Su solución debe eliminar todas las líneas que no sean más que espacios en blanco, así como la última línea nueva . Si hay una cadena que, después de ejecutar su expresión regular, termina con "/ r / n" o cualquier espacio en blanco , falla.
Aquí hay otra opción: use la clase StringReader
. Ventajas: un pase sobre la cuerda, no crea matrices intermedias.
public static string RemoveEmptyLines(this string text) {
var builder = new StringBuilder();
using (var reader = new StringReader(text)) {
while (reader.Peek() != -1) {
string line = reader.ReadLine();
if (!string.IsNullOrWhiteSpace(line))
builder.AppendLine(line);
}
}
return builder.ToString();
}
Nota: el método IsNullOrWhiteSpace
es nuevo en .NET 4.0 . Si no tiene eso, es trivial escribir por su cuenta:
public static bool IsNullOrWhiteSpace(string text) {
return string.IsNullOrEmpty(text) || text.Trim().Length < 1;
}
De acuerdo, esta respuesta está de acuerdo con los requisitos aclarados especificados en la recompensa:
También necesito eliminar cualquier nueva línea final, y mi Regex-fu está fallando. Mi recompensa va para cualquiera que pueda darme una expresión regular que pase esta prueba: StripWhitespace ("test / r / n / r / nthis / r / n / r / n") == "test / r / nthis"
Así que aquí está la respuesta:
(?<=/r?/n)(/s*$/r?/n)+|(?<=/r?/n)(/r?/n)+|(/r?/n)+/z
O en el código C # proporcionado por @Chris Schmich:
string fix = Regex.Replace("test/r/n /r/nthis/r/n/r/n", @"(?<=/r?/n)(/s*$/r?/n)+|(?<=/r?/n)(/r?/n)+|(/r?/n)+/z", string.Empty, RegexOptions.Multiline);
Ahora intentemos entenderlo. Hay tres patrones opcionales aquí que estoy dispuesto a reemplazar con string.empty
.
-
(?<=/r?/n)(/s*$/r?/n)+
- coincide con una a líneas ilimitadas que contienen solo espacio en blanco y precedida por un salto de línea (pero no coincide con los primeros saltos de línea precedentes). -
(?<=/r?/n)(/r?/n)+
- hace coincidir uno con líneas vacías ilimitadas sin contenido precedido por un salto de línea (pero no coincide con los primeros saltos de línea precedentes). -
(/r?/n)+/z
- hace coincidir uno con saltos de línea ilimitados al final de la cadena probada (saltos de línea consecutivos como los llamó)
¡Eso satisface tu prueba perfectamente! ¡Pero también satisface los estilos de salto de línea /r/n
y /n
! Pruébalo! Creo que esta será la respuesta más correcta, aunque la expresión más simple pasaría su prueba de recompensa específica, esta expresión regular pasa las condiciones más complejas.
EDITAR: @Will señaló un posible defecto en la última coincidencia de patrón de la expresión regular anterior en que no coincidirá con múltiples saltos de línea que contengan espacios en blanco al final de la cadena de prueba. Así que cambiemos ese último patrón a esto:
/b/s+/z
El / b es un boundry de palabra (principio o FIN de una palabra), el / s + es uno o más caracteres de espacio en blanco, el / z es el final de la cadena de prueba (final del "archivo") . Por lo tanto, ahora coincidirá con cualquier surtido de espacios en blanco al final del archivo, incluidas pestañas y espacios, además de retornos de carro y saltos de línea. Probé los dos casos de prueba provistos por @Will.
Entonces todos juntos ahora, deberían ser:
(?<=/r?/n)(/s*$/r?/n)+|(?<=/r?/n)(/r?/n)+|/b/s+/z
EDICIÓN # 2: Muy bien, hay un caso más posible que @Wil encontró que la última expresión regular no cubre. Ese caso son entradas que tienen saltos de línea al principio del archivo antes del contenido. Así que vamos a agregar un patrón más para que coincida con el principio del archivo.
/A/s+
- La /A
coincide con el principio del archivo, la /s+
coincide con uno o más caracteres de espacio en blanco.
Entonces ahora tenemos:
/A/s+|(?<=/r?/n)(/s*$/r?/n)+|(?<=/r?/n)(/r?/n)+|/b/s+/z
Así que ahora tenemos cuatro patrones para emparejar:
- espacios en blanco al comienzo del archivo,
- saltos de línea redundantes que contienen espacios en blanco, (por ejemplo:
/r/n /r/n/t/r/n
) - saltos de línea redundantes sin contenido, (por ejemplo:
/r/n/r/n
) - espacios en blanco al final del archivo
Eh. Bueno, después de todo eso, no pude encontrar uno que pudiera afectar a todos los casos de esquina que podría resolver. El siguiente es mi último conjuro de una expresión regular que tiras
- Todas las líneas vacías desde el comienzo de una cadena
- Sin incluir espacios al comienzo de la primera línea que no sea de espacios en blanco
- Todas las líneas vacías después de la primera línea sin espacios en blanco y antes de la última línea sin espacios en blanco
- Nuevamente, preservar todo el espacio en blanco al comienzo de cualquier línea que no sea espacio en blanco
- Todas las líneas vacías después de la última línea sin espacios en blanco, incluida la última nueva línea
(? <= (/ r / n) | ^) / s * / r / n | / r / n / s * $
que esencialmente dice:
- Inmediatamente despues
- El comienzo de la cadena O
- El final de la última línea.
- Haga coincidir la mayor cantidad posible de espacios en blanco contiguos que terminen en una nueva línea *
- O
- Coincidir con una nueva línea y tantos espacios en blanco contiguos como sea posible que finalicen al final de la cadena
La primera mitad captura todos los espacios en blanco al comienzo de la cadena hasta la primera línea sin espacios en blanco, o todo el espacio en blanco entre las líneas que no son espacios en blanco. La segunda mitad atrapa el espacio en blanco restante en la cadena, incluida la nueva línea de la última línea no en blanco.
Gracias a todos los que trataron de ayudar; sus respuestas me ayudaron a pensar en todo lo que necesitaba considerar al hacer correspondencias.
* (Esta expresión regular considera que una nueva línea es /r/n
, por lo que tendrá que ajustarse según el origen de la cadena. No es necesario configurar opciones para ejecutar la coincidencia).
En respuesta a la recompensa de Will, aquí hay un subgrupo Perl que responde correctamente al caso de prueba:
sub StripWhitespace {
my $str = shift;
print "''",$str,"''/n";
$str =~ s/(?:/R+/s+(/R)+)|(?:()/R+)$/$1/g;
print "''",$str,"''/n";
return $str;
}
StripWhitespace("test/r/n /r/nthis/r/n/r/n");
salida:
''test
this
''
''test
this''
Para no utilizar /R
, reemplácelo por [/r/n]
e invierta la alternativa. Este produce el mismo resultado:
$str =~ s/(?:(/S)[/r/n]+)|(?:[/r/n]+/s+([/r/n])+)/$1/g;
No hay necesidad de configuración especial ni soporte de múltiples líneas. Sin embargo, puedes agregar s
bandera si es obligatorio.
$str =~ s/(?:(/S)[/r/n]+)|(?:[/r/n]+/s+([/r/n])+)/$1/sg;
En respuesta a la recompensa de Will, que espera una solución que tome "test/r/n /r/nthis/r/n/r/n"
y emite "test/r/nthis"
, he encontrado una solución que hace uso de la agrupación atómica (también conocida como subexpresiones sin retroceso en MSDN). Recomiendo leer esos artículos para una mejor comprensión de lo que está sucediendo. En última instancia, el grupo atómico ayudó a igualar los caracteres de nueva línea que se quedaron atrás.
Use RegexOptions.Multiline
con este patrón:
^/s+(?!/B)|/s*(?>[/r/n]+)$
Aquí hay un ejemplo con algunos casos de prueba, incluidos algunos que reuní de los comentarios de Will en otras publicaciones, así como los míos.
string[] inputs =
{
"one/r/n /r/ntwo/r/n/t/r/n /r/n",
"test/r/n /r/nthis/r/n/r/n",
"/r/n/r/ntest!",
"/r/ntest/r/n ! test",
"/r/ntest /r/n ! "
};
string[] outputs =
{
"one/r/ntwo",
"test/r/nthis",
"test!",
"test/r/n ! test",
"test /r/n ! "
};
string pattern = @"^/s+(?!/B)|/s*(?>[/r/n]+)$";
for (int i = 0; i < inputs.Length; i++)
{
string result = Regex.Replace(inputs[i], pattern, "",
RegexOptions.Multiline);
Console.WriteLine(result == outputs[i]);
}
EDITAR: Para solucionar el problema del patrón que no pudo limpiar el texto con una combinación de espacios en blanco y líneas nuevas, agregué /s*
a la última parte de alternancia de la expresión regular. Mi patrón anterior era redundante y me di cuenta de que /s*
manejaría ambos casos.
Extensión de la cuerda
public static string UnPrettyJson(this string s)
{
try
{
// var jsonObj = Json.Decode(s);
// var sObject = Json.Encode(value); dont work well with array of strings c:[''a'',''b'',''c'']
object jsonObj = JsonConvert.DeserializeObject(s);
return JsonConvert.SerializeObject(jsonObj, Formatting.None);
}
catch (Exception e)
{
throw new Exception(
s + " Is Not a valid JSON ! (please validate it in http://www.jsoneditoronline.org )", e);
}
}
Iré con:
public static string RemoveEmptyLines(string value) {
using (StringReader reader = new StringReader(yourstring)) {
StringBuilder builder = new StringBuilder();
string line;
while ((line = reader.ReadLine()) != null) {
if (line.Trim().Length > 0)
builder.AppendLine(line);
}
return builder.ToString();
}
}
Prueba esto.
string s = "Test1" + Environment.NewLine + Environment.NewLine + "Test 2";
Console.WriteLine(s);
string result = s.Replace(Environment.NewLine, String.Empty);
Console.WriteLine(result);
Si desea eliminar las líneas que contienen espacios en blanco (tabulaciones, espacios), intente:
string fix = Regex.Replace(original, @"^/s*$/n", string.Empty, RegexOptions.Multiline);
Editar (para @Will): la solución más simple para recortar nuevas líneas finales sería usar TrimEnd
en la cadena resultante, por ejemplo:
string fix =
Regex.Replace(original, @"^/s*$/n", string.Empty, RegexOptions.Multiline)
.TrimEnd();
Usando LINQ:
var result = string.Join("/r/n",
multilineString.Split(new string[] { "/r/n" }, ...None)
.Where(s => !string.IsNullOrWhitespace(s)));
Si está tratando con grandes entradas y / o terminaciones de línea inconsistentes, debe usar un StringReader y hacer en su lugar la vieja escuela anterior con un bucle Foreach.
la parte superior de mi cabeza...
string fixed = Regex.Replace(input, "/s*(/n)","$1");
gira esto:
fdasdf asdf [tabs] [spaces] asdf
dentro de esto:
fdasdf asdf asdf
no está bien. Yo usaría este usando JSON.net:
var o = JsonConvert.DeserializeObject(prettyJson);
new minifiedJson = JsonConvert.SerializeObject(o, Formatting.None);
si son solo espacios en blanco por qué no usas el método de cadena C #
string yourstring = "A O P V 1.5";
yourstring.Replace(" ", string.empty);
el resultado será "AOPV1.5"
char[] delimiters = new char[] { ''/r'', ''/n'' };
string[] lines = value.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);
string result = string.Join(Environment.NewLine, lines)
s = Regex.Replace(s, @"^[^/n/S]*/n", "");
[^/n/S]
coincide con cualquier carácter que no sea un salto de línea o un carácter que no sea de espacio en blanco, por lo tanto, cualquier carácter de espacio en blanco, excepto /n
. Pero lo más probable es que los únicos personajes de los que tenga que preocuparse sean el espacio, la pestaña y el retorno de carro, así que esto también debería funcionar:
s = Regex.Replace(s, @"^[ /t/r]*/n", "");
Y si quieres que atrape la última línea, sin un salto de línea final:
s = Regex.Replace(s, @"^[ /t/r]*/n?", "");
string corrected =
System.Text.RegularExpressions.Regex.Replace(input, @"/n+", "/n");
string outputString;
using (StringReader reader = new StringReader(originalString)
using (StringWriter writer = new StringWriter())
{
string line;
while((line = reader.ReadLine()) != null)
{
if (line.Trim().Length > 0)
writer.WriteLine(line);
}
outputString = writer.ToString();
}